diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0e3ca2191814ee..34e18599e5beaf 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -112,4 +112,4 @@ /docs/area-owners.* @jeffhandley /docs/issue*.md @jeffhandley /.github/policies/ @jeffhandley @mkArtakMSFT -/.github/workflows/ @dotnet/runtime-infrastructure +/.github/workflows/ @jeffhandley @dotnet/runtime-infrastructure diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 308cb2d1a85b6b..f5e7799b30e2a2 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -2,5 +2,21 @@ General guidance: -- Please make sure to include the @dotnet/runtime-infrastructure group as a reviewer of your PRs. -- Do not use the `pull_request` event. Use `pull_request_target` instead, as documented in [Workflows in forked repositories](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflows-in-forked-repositories) and [pull_request_target](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request_target). +Please make sure to include the @dotnet/runtime-infrastructure group as a reviewer of your PRs. + +For workflows that are triggered by pull requests, refer to GitHub's documentation for the `pull_request` and `pull_request_target` events. The `pull_request_target` event is the more common use case in this repository as it runs the workflow in the context of the target branch instead of in the context of the pull request's fork or branch. However, workflows that need to consume the contents of the pull request need to use the `pull_request` event. There are security considerations with each of the events though. + +Most workflows are intended to run only in the `dotnet/runtime` repository and not in forks. To force workflow jobs to be skipped in forks, each job should apply an `if` statement that checks the repository name or owner. Either approach works, but checking only the repository owner allows the workflow to run in copies or forks withing the dotnet org. + +```yaml +jobs: + job-1: + # Do not run this job in forks + if: github.repository == 'dotnet/runtime' + + job-2: + # Do not run this job in forks outside the dotnet org + if: github.repository_owner == 'dotnet' +``` + +Refer to GitHub's [Workflows in forked repositories](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflows-in-forked-repositories) and [pull_request_target](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request_target) documentation for more information. diff --git a/.github/workflows/check-no-merge-label.yml b/.github/workflows/check-no-merge-label.yml index 37cc7adf20596b..55154410c2c09d 100644 --- a/.github/workflows/check-no-merge-label.yml +++ b/.github/workflows/check-no-merge-label.yml @@ -12,6 +12,7 @@ on: jobs: check-labels: + if: github.repository == 'dotnet/runtime' runs-on: ubuntu-latest steps: - name: Check 'NO-MERGE' label diff --git a/.github/workflows/check-service-labels.yml b/.github/workflows/check-service-labels.yml index 9726087abbf4dc..6cc9bfddd5c6b2 100644 --- a/.github/workflows/check-service-labels.yml +++ b/.github/workflows/check-service-labels.yml @@ -11,6 +11,7 @@ on: jobs: check-labels: + if: github.repository == 'dotnet/runtime' runs-on: ubuntu-latest steps: - name: Check 'Servicing-approved' label diff --git a/docs/coding-guidelines/clr-code-guide.md b/docs/coding-guidelines/clr-code-guide.md index 281156344ec3f0..11b79fea262300 100644 --- a/docs/coding-guidelines/clr-code-guide.md +++ b/docs/coding-guidelines/clr-code-guide.md @@ -107,7 +107,7 @@ Rules can either be imposed by invariants or team policy. "Team Policy" rules are rules we've established as "good practices" – for example, the rule that every function must declare a contract. While a missing contract here or there isn't a shipstopper, violating these rules is still heavily frowned upon and you should expect a bug filed against you unless you can supply a very compelling reason why your code needs an exemption. -Team policy rules are not necessarily less important than invariants. For example, the rule to use [safemath.h][safemath.h] rather that coding your own integer overflow check is a policy rule. But because it deals with security, we'd probably treat it as higher priority than a very obscure (non-security) related bug. +Team policy rules are not necessarily less important than invariants. For example, the rule to use [safemath.h][safemath.h] rather than coding your own integer overflow check is a policy rule. But because it deals with security, we'd probably treat it as higher priority than a very obscure (non-security) related bug. [safemath.h]: https://github.com/dotnet/runtime/blob/main/src/coreclr/inc/safemath.h @@ -185,7 +185,7 @@ Here's how to fix our buggy code fragment. GCPROTECT_END(); } -Notice the addition of the line GCPROTECT_BEGIN(a). GCPROTECT_BEGIN is a macro whose argument is any OBJECTREF-typed storage location (it has to be an expression that can you can legally apply the address-of (&) operator to.) GCPROTECT_BEGIN tells the GC two things: +Notice the addition of the line GCPROTECT_BEGIN(a). GCPROTECT_BEGIN is a macro whose argument is any OBJECTREF-typed storage location (it has to be an expression that you can legally apply the address-of (&) operator to.) GCPROTECT_BEGIN tells the GC two things: - The GC is not to discard any object referred to by the reference stored in local "a". - If the GC moves the object referred to by "a", it must update "a" to point to the new location. @@ -250,7 +250,7 @@ The following code fragment shows how handles are used. In practice, of course, { MethodTable *pMT = g_pObjectClass->GetMethodTable(); - //Another way is to use handles. Handles would be + // Another way is to use handles. Handles would be // wasteful for a case this simple but are useful // if you need to protect something for the long // term. @@ -270,7 +270,7 @@ The following code fragment shows how handles are used. In practice, of course, There are actually several flavors of handles. This section lists the most common ones. ([objecthandle.h][objecthandle.h] contains the complete list.) - **HNDTYPE_STRONG**: This is the default and acts like a normal reference. Created by calling CreateHandle(OBJECTREF). -- **HNDTYPE_WEAK_LONG**: Tracks an object as long as one strong reference to its exists but does not itself prevent the object from being GC'd. Created by calling CreateWeakHandle(OBJECTREF). +- **HNDTYPE_WEAK_LONG**: Tracks an object as long as one strong reference to it exists but does not itself prevent the object from being GC'd. Created by calling CreateWeakHandle(OBJECTREF). - **HNDTYPE_PINNED**: Pinned handles are strong handles which have the added property that they prevent an object from moving during a garbage collection cycle. This is useful when passing a pointer to object innards out of the runtime while GC may be enabled. NOTE: PINNING AN OBJECT IS EXPENSIVE AS IT PREVENTS THE GC FROM ACHIEVING OPTIMAL PACKING OF OBJECTS DURING EPHEMERAL COLLECTIONS. THIS TYPE OF HANDLE SHOULD BE USED SPARINGLY! @@ -298,7 +298,7 @@ Now, while the compiler can generate any valid code for this, it's very likely i push [A] ;push argument to DoSomething call DoSomething -This is supposed to be work correctly in every case, according to the semantics of GCPROTECT. However, suppose just after the "push" instruction, another thread gets the time-slice, starts a GC and moves the object A. The local variable A will be correctly updated – but the copy of A which we just pushed as an argument to DoSomething() will not. Hence, DoSomething() will receive a pointer to the old location and crash. Clearly, preemptive GC alone will not suffice for the CLR. +This is supposed to work correctly in every case, according to the semantics of GCPROTECT. However, suppose just after the "push" instruction, another thread gets the time-slice, starts a GC and moves the object A. The local variable A will be correctly updated – but the copy of A which we just pushed as an argument to DoSomething() will not. Hence, DoSomething() will receive a pointer to the old location and crash. Clearly, preemptive GC alone will not suffice for the CLR. How about the alternative: cooperative GC? With cooperative GC, the above problem doesn't occur and GCPROTECT works as intended. Unfortunately, the CLR has to interop with legacy unmanaged code as well. Suppose a managed app calls out to the Win32 MessageBox api which waits for the user to click a button before returning. Until the user does this, all managed threads in the same process are blocked from GC. Not good. @@ -447,7 +447,7 @@ Why do we use GC_NOTRIGGERS rather than GC_FORBID? Because forcing every functio **Note:** There is no GC_FORBID keyword defined for contracts but you can simulate it by combining GC_NOTRIGGER and MODE_COOPERATIVE. -**Important:** The notrigger thread state is implemented as a counter rather than boolean. This is unfortunate as this should not be necessary and exposes us to nasty ref-counting style bugs. What is important that contracts intentionally do not support unscoped trigger/notrigger transitions. That is, a GC_NOTRIGGER inside a contract will **increment** the thread's notrigger count on entry to the function but on exit, **it will not decrement the count , instead it will restore the count from a saved value.** Thus, any _net_ changes in the trigger state caused within the body of the function will be wiped out. This is good unless your function was designed to make a net change to the trigger state. If you have such a need, you'll just have to work around it somehow because we actively discourage such things in the first place. Ideally, we'd love to replace that counter with a Boolean at sometime. +**Important:** The notrigger thread state is implemented as a counter rather than boolean. This is unfortunate as this should not be necessary and exposes us to nasty ref-counting style bugs. What is important is that contracts intentionally do not support unscoped trigger/notrigger transitions. That is, a GC_NOTRIGGER inside a contract will **increment** the thread's notrigger count on entry to the function but on exit, **it will not decrement the count , instead it will restore the count from a saved value.** Thus, any _net_ changes in the trigger state caused within the body of the function will be wiped out. This is good unless your function was designed to make a net change to the trigger state. If you have such a need, you'll just have to work around it somehow because we actively discourage such things in the first place. Ideally, we'd love to replace that counter with a Boolean at sometime. #### 2.1.10.1 GC_NOTRIGGER/TRIGGERSGC on a scope @@ -467,13 +467,13 @@ One difference between the standalone TRIGGERSGC and the contract GC_TRIGGERS: t ### 2.2.1 What are holders and why are they important? -The CLR team has coined the name **holder** to refer to the infrastructure that encapsulates the common grunt work of writing robust **backout code**. **Backout code** is code that deallocate resources or restore CLR data structure consistency when we abort an operation due to an error or an asynchronous event. Oftentimes, the same backout code will execute in non-error paths for resources allocated for use of a single scope, but error-time backout is still needed even for longer lived resources. +The CLR team has coined the name **holder** to refer to the infrastructure that encapsulates the common grunt work of writing robust **backout code**. **Backout code** is code that deallocates resources or restores CLR data structure consistency when we abort an operation due to an error or an asynchronous event. Oftentimes, the same backout code will execute in non-error paths for resources allocated for use of a single scope, but error-time backout is still needed even for longer lived resources. Way back in V1, error paths were _ad-hoc._ Typically, they flowed through "fail:" labels where the backout code was accumulated. Due to the no-compromise robustness requirements that the CLR Hosting model (with SQL Server as the initial customer) imposed on us in the .NET Framework v2 release, we have since become much more formal about backout. One reason is that we like to write backout that will execute if you leave the scope because of an exception. We also want to centralize policy regarding exceptions occurring inside backout. Finally, we want an infrastructure that will discourage developer errors from introducing backout bugs in the first place. -Thus, we have centralized cleanup around C++ destructor technology. Instead of declaring a HANDLE, you declare a HandleHolder. The holder wraps a HANDLE and its destructor closes the handle no matter how control leaves the scope. We have already implemented standard holders for common resources (arrays, memory allocated with C++ new, Win32 handles and locks.) The Holder mechanism is extensible so you can add new types of holders as you need them. +Thus, we have centralized cleanup around C++ destructor technology. Instead of declaring a HANDLE, you declare a HandleHolder. The holder wraps a HANDLE and its destructor closes the handle no matter how control leaves the scope. We have already implemented standard holders for common resources (arrays, memory allocated with C++ new, Win32 handles, and locks.) The Holder mechanism is extensible so you can add new types of holders as you need them. ### 2.2.2 An example of holder usage @@ -707,8 +707,8 @@ If you wish to set the OOM state for a scope rather than a function, use the FAU #### 2.3.2.3 Remember... - Do not use INJECT_FAULT to indicate the possibility of non-OOM errors such as entries not existing in a hash table or a COM object not supporting an interface. INJECT_FAULT indicates OOM errors and no other type. -- Be very suspicious if your INJECT_FAULT() argument is anything other than throwing an OOM exception or returning E_OUTOFMEMORY. OOM errors must distinguishable from other types of errors so if you're merely returning NULL without indicating the type of error, you'd better be a simple memory allocator or some other function that will never fail for any reason other than an OOM. -- THROWS and INJECT_FAULT correlate strongly but are independent. A NOTHROW/INJECT_FAULT combo might indicate a function that returns HRESULTs including E_OUTOFMEMORY. A THROWS/FORBID_FAULT however indicate a function that can throw an exception but not an OutOfMemoryException. While theoretically possible, such a contract is probably a bug. +- Be very suspicious if your INJECT_FAULT() argument is anything other than throwing an OOM exception or returning E_OUTOFMEMORY. OOM errors must be distinguishable from other types of errors so if you're merely returning NULL without indicating the type of error, you'd better be a simple memory allocator or some other function that will never fail for any reason other than an OOM. +- THROWS and INJECT_FAULT correlate strongly but are independent. A NOTHROW/INJECT_FAULT combo might indicate a function that returns HRESULTs including E_OUTOFMEMORY. A THROWS/FORBID_FAULT however indicates a function that can throw an exception but not an OutOfMemoryException. While theoretically possible, such a contract is probably a bug. ## 2.4 Are you using SString and/or the safe string manipulation functions? @@ -716,7 +716,7 @@ The native C implementation of strings as raw char* buffers is a well-known bree ### 2.4.1 SString -SString is the abstraction to use for unmanaged strings in CLR code. It is important that as much code is possible uses the SString abstraction rather than raw character arrays, because of the danger of buffer overrun related to direct manipulation of arrays. Code which does not use SString must be manually reviewed for the possibility of buffer overrun or corruption during every security review. +SString is the abstraction to use for unmanaged strings in CLR code. It is important that as much code as possible uses the SString abstraction rather than raw character arrays, because of the danger of buffer overrun related to direct manipulation of arrays. Code which does not use SString must be manually reviewed for the possibility of buffer overrun or corruption during every security review. This section will provide an overview for SString. For specific details on methods and use, see the file [src\inc\sstring.h][sstring.h]. SString has been in use in our codebase for quite a few years now so examples of its use should be easy to find. @@ -724,7 +724,7 @@ This section will provide an overview for SString. For specific details on metho An SString object represents a Unicode string. It has its own buffer which it internally manages. The string buffer is typically not referenced directly by user code; instead the string is manipulated indirectly by methods defined on SString. Ultimately there are several ways to get at the raw string buffer if such functionality is needed to interface to existing APIs. But these should be used only when absolutely necessary. -When SStrings are used as local variables, they are typically used via the StackSString type, which uses a bit of stack space as a preallocated buffer for optimization purposes. When SStrings are use in structures, the SString type may be used directly (if it is likely that the string will be empty), or through the InlineSString template, which allows an arbitrary amount of preallocated space to be declared inline in the structure with the SString. Since InlineSStrings and StackSStrings are subtypes of SString, they have the same API, and can be passed wherever an SString is required. +When SStrings are used as local variables, they are typically used via the StackSString type, which uses a bit of stack space as a preallocated buffer for optimization purposes. When SStrings are used in structures, the SString type may be used directly (if it is likely that the string will be empty), or through the InlineSString template, which allows an arbitrary amount of preallocated space to be declared inline in the structure with the SString. Since InlineSStrings and StackSStrings are subtypes of SString, they have the same API, and can be passed wherever an SString is required. As parameters, SStrings should always be declared as reference parameters. Similarly, SStrings as return value should also use a "by reference" style. diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index d4109bba4deda3..493889cf360c37 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -60,14 +60,14 @@ dfa03011d6474bd0e6c9d0363e4f3b18b99f2ad8 - + https://github.com/dotnet/emsdk - 191ba073d94b647f70bce142d16ec96bfd436387 + dd097e361bffff02e11fceab854f6156027acee9 - + https://github.com/dotnet/emsdk - 191ba073d94b647f70bce142d16ec96bfd436387 + dd097e361bffff02e11fceab854f6156027acee9 @@ -376,17 +376,17 @@ https://github.com/dotnet/runtime-assets fc476e8f2d685eb7cadf6342393a0af2708f4dbf - + https://github.com/dotnet/roslyn - b5e48cce69950f6f8cf8b6c6919cddf4c03a2f48 + a6c751e3b34ba2cb87ff3761d1ba89dcb4f3069f - + https://github.com/dotnet/roslyn - b5e48cce69950f6f8cf8b6c6919cddf4c03a2f48 + a6c751e3b34ba2cb87ff3761d1ba89dcb4f3069f - + https://github.com/dotnet/roslyn - b5e48cce69950f6f8cf8b6c6919cddf4c03a2f48 + a6c751e3b34ba2cb87ff3761d1ba89dcb4f3069f https://github.com/dotnet/roslyn-analyzers @@ -397,9 +397,9 @@ 8fe7aeb135c64e095f43292c427453858d937184 - + https://github.com/dotnet/roslyn - b5e48cce69950f6f8cf8b6c6919cddf4c03a2f48 + a6c751e3b34ba2cb87ff3761d1ba89dcb4f3069f @@ -432,37 +432,37 @@ https://github.com/NuGet/NuGet.Client 8fef55f5a55a3b4f2c96cd1a9b5ddc51d4b927f8 - + https://github.com/dotnet/node - 7609fd62519260f6b2c10131b03ed878e671a2e4 + f157b219c30f5296cb3ffaa6937ef19d702c5aab - + https://github.com/dotnet/node - 7609fd62519260f6b2c10131b03ed878e671a2e4 + f157b219c30f5296cb3ffaa6937ef19d702c5aab - + https://github.com/dotnet/node - 7609fd62519260f6b2c10131b03ed878e671a2e4 + f157b219c30f5296cb3ffaa6937ef19d702c5aab - + https://github.com/dotnet/node - 7609fd62519260f6b2c10131b03ed878e671a2e4 + f157b219c30f5296cb3ffaa6937ef19d702c5aab - + https://github.com/dotnet/node - 7609fd62519260f6b2c10131b03ed878e671a2e4 + f157b219c30f5296cb3ffaa6937ef19d702c5aab - + https://github.com/dotnet/node - 7609fd62519260f6b2c10131b03ed878e671a2e4 + f157b219c30f5296cb3ffaa6937ef19d702c5aab - + https://github.com/dotnet/node - 7609fd62519260f6b2c10131b03ed878e671a2e4 + f157b219c30f5296cb3ffaa6937ef19d702c5aab - + https://github.com/dotnet/node - 7609fd62519260f6b2c10131b03ed878e671a2e4 + f157b219c30f5296cb3ffaa6937ef19d702c5aab https://github.com/dotnet/runtime-assets diff --git a/eng/Versions.props b/eng/Versions.props index a2d4fc5c5c2006..c404ab8efcccad 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -44,9 +44,9 @@ Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure they do not break the local dev experience. --> - 4.14.0-2.25081.4 - 4.14.0-2.25081.4 - 4.14.0-2.25081.4 + 4.14.0-2.25104.11 + 4.14.0-2.25104.11 + 4.14.0-2.25104.11 - 10.0.0-preview.2.25103.1 + 10.0.0-preview.2.25104.1 $(MicrosoftNETWorkloadEmscriptenCurrentManifest100100TransportVersion) 1.1.87-gba258badda @@ -270,7 +270,7 @@ 1.0.406601 $(MicrosoftDotNetApiCompatTaskVersion) - 10.0.0-alpha.1.25077.1 + 10.0.0-alpha.1.25103.1 $(MicrosoftNETRuntimeEmscriptenVersion) $(runtimewinx64MicrosoftNETCoreRuntimeWasmNodeTransportPackageVersion) diff --git a/eng/pipelines/common/templates/runtimes/build-runtime-tests-and-send-to-helix.yml b/eng/pipelines/common/templates/runtimes/build-runtime-tests-and-send-to-helix.yml index db0ab302b43545..26b86483cd8e28 100644 --- a/eng/pipelines/common/templates/runtimes/build-runtime-tests-and-send-to-helix.yml +++ b/eng/pipelines/common/templates/runtimes/build-runtime-tests-and-send-to-helix.yml @@ -19,8 +19,7 @@ parameters: variables: {} pool: '' dependsOn: [] - compileOnHelix: false - interpreter: false + extraHelixArguments: '' buildAllTestsAsStandalone: false #arcade-specific parameters condition: always() @@ -84,56 +83,41 @@ steps: env: __MonoToolPrefix: aarch64-linux-gnu- - # Checks the value of the compileOnHelix parameter - # and if set invokes libraries pipeline for AOT on Helix - - ${{ if eq(parameters.compileOnHelix, 'true') }}: - - template: /eng/pipelines/libraries/helix.yml - parameters: - osGroup: ${{ parameters.osGroup }} - runtimeFlavor: ${{ parameters.runtimeFlavor }} - archType: ${{ parameters.archType }} - targetRid: ${{ parameters.targetRid }} - buildConfig: ${{ parameters.buildConfig }} - interpreter: ${{ parameters.interpreter }} - testRunNamePrefixSuffix: ${{ parameters.testRunNamePrefixSuffix }} - extraHelixArguments: ${{ parameters.extraHelixArguments }} - helixQueues: ${{ parameters.helixQueues }} - creator: ${{ parameters.creator }} - - ${{ else }}: - - template: /eng/pipelines/common/templates/runtimes/send-to-helix-step.yml - parameters: - displayName: Send tests to Helix - buildConfig: $(buildConfigUpper) - archType: ${{ parameters.archType }} - osGroup: ${{ parameters.osGroup }} - osSubgroup: ${{ parameters.osSubgroup}} - coreClrRepoRoot: $(Build.SourcesDirectory)/src/coreclr - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - runtimeFlavor: ${{ parameters.runtimeFlavor }} - runtimeVariant: ${{ parameters.runtimeVariant }} + - template: /eng/pipelines/common/templates/runtimes/send-to-helix-step.yml + parameters: + displayName: Send tests to Helix + buildConfig: $(buildConfigUpper) + archType: ${{ parameters.archType }} + osGroup: ${{ parameters.osGroup }} + osSubgroup: ${{ parameters.osSubgroup}} + coreClrRepoRoot: $(Build.SourcesDirectory)/src/coreclr + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + runtimeFlavor: ${{ parameters.runtimeFlavor }} + runtimeVariant: ${{ parameters.runtimeVariant }} - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - # Access token variable for internal project from the - # DotNet-HelixApi-Access variable group - helixAccessToken: $(HelixApiAccessToken) - ${{ else }}: - creator: $(Build.DefinitionName) + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + # Access token variable for internal project from the + # DotNet-HelixApi-Access variable group + helixAccessToken: $(HelixApiAccessToken) + ${{ else }}: + creator: $(Build.DefinitionName) - helixBuild: $(Build.BuildNumber) - helixSource: $(_HelixSource) - ${{ if ne(parameters.readyToRun, true) }}: - helixType: 'test/functional/cli/' + helixBuild: $(Build.BuildNumber) + helixSource: $(_HelixSource) + ${{ if ne(parameters.readyToRun, true) }}: + helixType: 'test/functional/cli/' - helixQueues: ${{ parameters.helixQueues }} - # This tests whether an array is empty - ${{ if eq(join('', parameters.helixQueues), '') }}: - condition: false - publishTestResults: true - timeoutPerTestInMinutes: $(timeoutPerTestInMinutes) - timeoutPerTestCollectionInMinutes: $(timeoutPerTestCollectionInMinutes) - runCrossGen2: ${{ eq(parameters.readyToRun, true) }} - compositeBuildMode: ${{ parameters.compositeBuildMode }} - runInUnloadableContext: ${{ parameters.runInUnloadableContext }} - nativeAotTest: ${{ parameters.nativeAotTest }} - helixProjectArguments: '$(Build.SourcesDirectory)/src/tests/Common/helixpublishwitharcade.proj' - scenarios: ${{ parameters.scenarios }} + helixQueues: ${{ parameters.helixQueues }} + # This tests whether an array is empty + ${{ if eq(join('', parameters.helixQueues), '') }}: + condition: false + publishTestResults: true + timeoutPerTestInMinutes: $(timeoutPerTestInMinutes) + timeoutPerTestCollectionInMinutes: $(timeoutPerTestCollectionInMinutes) + runCrossGen2: ${{ eq(parameters.readyToRun, true) }} + compositeBuildMode: ${{ parameters.compositeBuildMode }} + runInUnloadableContext: ${{ parameters.runInUnloadableContext }} + nativeAotTest: ${{ parameters.nativeAotTest }} + helixProjectArguments: '$(Build.SourcesDirectory)/src/tests/Common/helixpublishwitharcade.proj' + extraHelixArguments: ${{ parameters.extraHelixArguments }} + scenarios: ${{ parameters.scenarios }} diff --git a/eng/pipelines/common/templates/runtimes/send-to-helix-step.yml b/eng/pipelines/common/templates/runtimes/send-to-helix-step.yml index a45c3817d13b53..989ccdc9319a92 100644 --- a/eng/pipelines/common/templates/runtimes/send-to-helix-step.yml +++ b/eng/pipelines/common/templates/runtimes/send-to-helix-step.yml @@ -19,6 +19,7 @@ parameters: runCrossGen2: '' compositeBuildMode: false helixProjectArguments: '' + extraHelixArguments: '' runInUnloadableContext: '' tieringTest: '' hotColdSplitting: '' @@ -38,7 +39,7 @@ steps: - template: send-to-helix-inner-step.yml parameters: osGroup: ${{ parameters.osGroup }} - sendParams: ${{ parameters.helixProjectArguments }} ${{ parameters.msbuildParallelism }} /bl:$(Build.SourcesDirectory)/artifacts/log/SendToHelix.binlog /p:TargetArchitecture=${{ parameters.archType }} /p:TargetOS=${{ parameters.osGroup }} /p:TargetOSSubgroup=${{ parameters.osSubgroup }} /p:Configuration=${{ parameters.buildConfig }} + sendParams: ${{ parameters.helixProjectArguments }} ${{ parameters.msbuildParallelism }} /bl:$(Build.SourcesDirectory)/artifacts/log/SendToHelix.binlog /p:TargetArchitecture=${{ parameters.archType }} /p:TargetOS=${{ parameters.osGroup }} /p:TargetOSSubgroup=${{ parameters.osSubgroup }} /p:Configuration=${{ parameters.buildConfig }} ${{ parameters.extraHelixArguments }} condition: and(succeeded(), ${{ parameters.condition }}) shouldContinueOnError: ${{ parameters.shouldContinueOnError }} displayName: ${{ parameters.displayName }} diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslike.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslike.yml index c123ea162e4d08..47a38df343f80d 100644 --- a/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslike.yml +++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslike.yml @@ -91,9 +91,8 @@ jobs: - template: /eng/pipelines/common/templates/runtimes/build-runtime-tests-and-send-to-helix.yml parameters: creator: dotnet-bot + testBuildArgs: /p:DevTeamProvisioning=- /p:RunAOTCompilation=true /p:MonoForceInterpreter=true /p:BuildTestsOnHelix=true compileOnHelix: true - interpreter: true - testBuildArgs: /p:ArchiveTests=true /p:DevTeamProvisioning=- /p:RunAOTCompilation=true /p:MonoForceInterpreter=true /p:BuildTestsOnHelix=true testRunNamePrefixSuffix: Mono_$(_BuildConfig) extraHelixArguments: /p:NeedsToBuildAppsOnHelix=true diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslikesimulator.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslikesimulator.yml index 714acd5b9c159f..2cfb553d5dbd6e 100644 --- a/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslikesimulator.yml +++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslikesimulator.yml @@ -89,9 +89,8 @@ jobs: - template: /eng/pipelines/common/templates/runtimes/build-runtime-tests-and-send-to-helix.yml parameters: creator: dotnet-bot + testBuildArgs: /p:DevTeamProvisioning=- /p:RunAOTCompilation=true /p:MonoForceInterpreter=true /p:BuildTestsOnHelix=true compileOnHelix: true - interpreter: true - testBuildArgs: /p:ArchiveTests=true /p:DevTeamProvisioning=- /p:RunAOTCompilation=true /p:MonoForceInterpreter=true /p:BuildTestsOnHelix=true testRunNamePrefixSuffix: Mono_$(_BuildConfig) extraHelixArguments: /p:NeedsToBuildAppsOnHelix=true diff --git a/eng/testing/tests.ioslike.targets b/eng/testing/tests.ioslike.targets index e2e36415a31834..6e7d667bdfe020 100644 --- a/eng/testing/tests.ioslike.targets +++ b/eng/testing/tests.ioslike.targets @@ -28,6 +28,7 @@ <_AppleBuildCommand Condition="'$(IncludesTestRunner)' == 'true'">apple test <_AppleBuildCommand Condition="'$(IncludesTestRunner)' != 'true'">apple run + <_AppleExpectedExitCode Condition="'$(ExpectedExitCode)' != ''">--expected-exit-code $(ExpectedExitCode) <_AfterBuildCommands> mv $XHARNESS_OUT/AOTBuild.binlog "$HELIX_WORKITEM_UPLOAD_ROOT" @@ -178,7 +179,7 @@ true true true - AppleTestRunner.dll + AppleTestRunner.dll $(PublishDir) $(BundleDir) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 8cc80f91b48b09..b8482f4592ae9a 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -8292,7 +8292,7 @@ const StructSegments& Compiler::GetSignificantSegments(ClassLayout* layout) StructSegments* newSegments = new (this, CMK_Promotion) StructSegments(getAllocator(CMK_Promotion)); - if (layout->IsBlockLayout()) + if (layout->IsCustomLayout()) { newSegments->Add(StructSegments::Segment(0, layout->GetSize())); } diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 6a7298cf3d2373..2bcbe0ee2bd31d 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -1938,12 +1938,16 @@ class FlowGraphDfsTree // Whether the DFS that produced the tree found any backedges. bool m_hasCycle; + // Whether the DFS that produced the tree used edge likelihoods to influence successor visitation order. + bool m_profileAware; + public: - FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle) + FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware) : m_comp(comp) , m_postOrder(postOrder) , m_postOrderCount(postOrderCount) , m_hasCycle(hasCycle) + , m_profileAware(profileAware) { } @@ -1978,6 +1982,11 @@ class FlowGraphDfsTree return m_hasCycle; } + bool IsProfileAware() const + { + return m_profileAware; + } + #ifdef DEBUG void Dump() const; #endif // DEBUG @@ -9246,7 +9255,7 @@ class Compiler bool isOpaqueSIMDType(ClassLayout* layout) const { - if (layout->IsBlockLayout()) + if (layout->IsCustomLayout()) { return true; } @@ -11104,14 +11113,16 @@ class Compiler ClassLayout* typGetLayoutByNum(unsigned layoutNum); // Get the layout number of the specified layout. unsigned typGetLayoutNum(ClassLayout* layout); + // Get the layout for the specified class handle. + ClassLayout* typGetObjLayout(CORINFO_CLASS_HANDLE classHandle); + // Get the number of a layout for the specified class handle. + unsigned typGetObjLayoutNum(CORINFO_CLASS_HANDLE classHandle); + ClassLayout* typGetCustomLayout(const ClassLayoutBuilder& builder); + unsigned typGetCustomLayoutNum(const ClassLayoutBuilder& builder); // Get the layout having the specified size but no class handle. ClassLayout* typGetBlkLayout(unsigned blockSize); // Get the number of a layout having the specified size but no class handle. unsigned typGetBlkLayoutNum(unsigned blockSize); - // Get the layout for the specified class handle. - ClassLayout* typGetObjLayout(CORINFO_CLASS_HANDLE classHandle); - // Get the number of a layout for the specified class handle. - unsigned typGetObjLayoutNum(CORINFO_CLASS_HANDLE classHandle); var_types TypeHandleToVarType(CORINFO_CLASS_HANDLE handle, ClassLayout** pLayout = nullptr); var_types TypeHandleToVarType(CorInfoType jitType, CORINFO_CLASS_HANDLE handle, ClassLayout** pLayout = nullptr); diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index 1794c6b57954c7..824c974067184a 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -4742,15 +4742,30 @@ void Compiler::fgDebugCheckFlowGraphAnnotations() return; } - unsigned count = fgRunDfs( - [](BasicBlock* block, unsigned preorderNum) { + auto visitPreorder = [](BasicBlock* block, unsigned preorderNum) { assert(block->bbPreorderNum == preorderNum); - }, - [=](BasicBlock* block, unsigned postorderNum) { + }; + + auto visitPostorder = [=](BasicBlock* block, unsigned postorderNum) { assert(block->bbPostorderNum == postorderNum); assert(m_dfsTree->GetPostOrder(postorderNum) == block); - }, - [](BasicBlock* block, BasicBlock* succ) {}); + }; + + auto visitEdge = [](BasicBlock* block, BasicBlock* succ) {}; + + unsigned count; + if (m_dfsTree->IsProfileAware()) + { + count = fgRunDfs(visitPreorder, + visitPostorder, + visitEdge); + } + else + { + count = fgRunDfs(visitPreorder, + visitPostorder, + visitEdge); + } assert(m_dfsTree->GetPostOrderCount() == count); diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index 8c8376694281f6..064c94d6af4107 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -4663,10 +4663,6 @@ void Compiler::fgDoReversePostOrderLayout() } #endif // DEBUG - // Compute DFS of all blocks in the method, using profile data to determine the order successors are visited in. - // - m_dfsTree = fgComputeDfs(); - // If LSRA didn't create any new blocks, we can reuse its loop-aware RPO traversal, // which is cached in Compiler::fgBBs. // If the cache isn't available, we need to recompute the loop-aware RPO. @@ -4675,15 +4671,21 @@ void Compiler::fgDoReversePostOrderLayout() if (rpoSequence == nullptr) { - rpoSequence = new (this, CMK_BasicBlock) BasicBlock*[m_dfsTree->GetPostOrderCount()]; + assert(m_dfsTree == nullptr); + m_dfsTree = fgComputeDfs(); FlowGraphNaturalLoops* const loops = FlowGraphNaturalLoops::Find(m_dfsTree); - unsigned index = 0; - auto addToSequence = [rpoSequence, &index](BasicBlock* block) { + rpoSequence = new (this, CMK_BasicBlock) BasicBlock*[m_dfsTree->GetPostOrderCount()]; + unsigned index = 0; + auto addToSequence = [rpoSequence, &index](BasicBlock* block) { rpoSequence[index++] = block; }; fgVisitBlocksInLoopAwareRPO(m_dfsTree, loops, addToSequence); } + else + { + assert(m_dfsTree != nullptr); + } // Fast path: We don't have any EH regions, so just reorder the blocks // diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index eaed687a57eee2..369b7b7a30241c 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -4353,7 +4353,7 @@ FlowGraphDfsTree* Compiler::fgComputeDfs() fgRunDfs(visitPreorder, visitPostorder, visitEdge); - return new (this, CMK_DepthFirstSearch) FlowGraphDfsTree(this, postOrder, numBlocks, hasCycle); + return new (this, CMK_DepthFirstSearch) FlowGraphDfsTree(this, postOrder, numBlocks, hasCycle, useProfile); } // Add explicit instantiations. diff --git a/src/coreclr/jit/layout.cpp b/src/coreclr/jit/layout.cpp index 655750718bc734..5c9105b04072d6 100644 --- a/src/coreclr/jit/layout.cpp +++ b/src/coreclr/jit/layout.cpp @@ -5,6 +5,65 @@ #include "layout.h" #include "compiler.h" +// Key used in ClassLayoutTable's hash table for custom layouts. +struct CustomLayoutKey +{ + unsigned Size; + const BYTE* GCPtrTypes; + + CustomLayoutKey(ClassLayout* layout) + : Size(layout->GetSize()) + , GCPtrTypes(layout->m_gcPtrCount > 0 ? layout->GetGCPtrs() : nullptr) + { + assert(layout->IsCustomLayout()); + } + + CustomLayoutKey(const ClassLayoutBuilder& builder) + : Size(builder.m_size) + , GCPtrTypes(builder.m_gcPtrCount > 0 ? builder.m_gcPtrs : nullptr) + { + } + + static bool Equals(const CustomLayoutKey& l, const CustomLayoutKey& r) + { + if (l.Size != r.Size) + { + return false; + } + + if ((l.GCPtrTypes == nullptr) != (r.GCPtrTypes == nullptr)) + { + return false; + } + + if ((l.GCPtrTypes != nullptr) && (memcmp(l.GCPtrTypes, r.GCPtrTypes, l.Size / TARGET_POINTER_SIZE) != 0)) + { + return false; + } + + return true; + } + + static unsigned GetHashCode(const CustomLayoutKey& key) + { + unsigned hash = key.Size; + if (key.GCPtrTypes != nullptr) + { + hash ^= 0xc4cfbb2a + (hash << 19) + (hash >> 13); + for (unsigned i = 0; i < key.Size / TARGET_POINTER_SIZE; i++) + { + hash ^= key.GCPtrTypes[i] + 0x9e3779b9 + (hash << 19) + (hash >> 13); + } + } + else + { + hash ^= 0x324ba6da + (hash << 19) + (hash >> 13); + } + + return hash; + } +}; + // Keeps track of layout objects associated to class handles or block sizes. A layout is usually // referenced by a pointer (ClassLayout*) but can also be referenced by a number (unsigned, // FirstLayoutNum-based), when space constraints or other needs make numbers more appealing. @@ -18,7 +77,7 @@ class ClassLayoutTable static constexpr unsigned ZeroSizedBlockLayoutNum = TYP_UNKNOWN + 1; static constexpr unsigned FirstLayoutNum = TYP_UNKNOWN + 2; - typedef JitHashTable, unsigned> BlkLayoutIndexMap; + typedef JitHashTable CustomLayoutIndexMap; typedef JitHashTable, unsigned> ObjLayoutIndexMap; union @@ -29,15 +88,15 @@ class ClassLayoutTable // Otherwise a dynamic array is allocated and hashtables are used to map from handle/size to layout array index. struct { - ClassLayout** m_layoutLargeArray; - BlkLayoutIndexMap* m_blkLayoutMap; - ObjLayoutIndexMap* m_objLayoutMap; + ClassLayout** m_layoutLargeArray; + CustomLayoutIndexMap* m_customLayoutMap; + ObjLayoutIndexMap* m_objLayoutMap; }; }; // The number of layout objects stored in this table. - unsigned m_layoutCount; + unsigned m_layoutCount = 0; // The capacity of m_layoutLargeArray (when more than 3 layouts are stored). - unsigned m_layoutLargeCapacity; + unsigned m_layoutLargeCapacity = 0; // We furthermore fast-path the 0-sized block layout which is used for // block locals that may grow (e.g. the outgoing arg area in every non-x86 // compilation). @@ -45,9 +104,7 @@ class ClassLayoutTable public: ClassLayoutTable() - : m_layoutCount(0) - , m_layoutLargeCapacity(0) - , m_zeroSizedBlockLayout(0) + : m_zeroSizedBlockLayout(0) { } @@ -76,25 +133,25 @@ class ClassLayoutTable } // Get the layout having the specified size but no class handle. - ClassLayout* GetBlkLayout(Compiler* compiler, unsigned blockSize) + ClassLayout* GetCustomLayout(Compiler* compiler, const ClassLayoutBuilder& builder) { - if (blockSize == 0) + if (builder.m_size == 0) { return &m_zeroSizedBlockLayout; } - return GetLayoutByIndex(GetBlkLayoutIndex(compiler, blockSize)); + return GetLayoutByIndex(GetCustomLayoutIndex(compiler, builder)); } // Get a number that uniquely identifies a layout having the specified size but no class handle. - unsigned GetBlkLayoutNum(Compiler* compiler, unsigned blockSize) + unsigned GetCustomLayoutNum(Compiler* compiler, const ClassLayoutBuilder& builder) { - if (blockSize == 0) + if (builder.m_size == 0) { return ZeroSizedBlockLayoutNum; } - return GetBlkLayoutIndex(compiler, blockSize) + FirstLayoutNum; + return GetCustomLayoutIndex(compiler, builder) + FirstLayoutNum; } // Get the layout for the specified class handle. @@ -147,8 +204,8 @@ class ClassLayoutTable else { unsigned index = 0; - if ((layout->IsBlockLayout() && m_blkLayoutMap->Lookup(layout->GetSize(), &index)) || - m_objLayoutMap->Lookup(layout->GetClassHandle(), &index)) + if (layout->IsCustomLayout() ? m_customLayoutMap->Lookup(CustomLayoutKey(layout), &index) + : m_objLayoutMap->Lookup(layout->GetClassHandle(), &index)) { return index; } @@ -157,16 +214,19 @@ class ClassLayoutTable unreached(); } - unsigned GetBlkLayoutIndex(Compiler* compiler, unsigned blockSize) + unsigned GetCustomLayoutIndex(Compiler* compiler, const ClassLayoutBuilder& builder) { - // The 0-sized block layout has its own fast path. - assert(blockSize != 0); + // The 0-sized layout has its own fast path. + assert(builder.m_size != 0); + + CustomLayoutKey key(builder); if (HasSmallCapacity()) { for (unsigned i = 0; i < m_layoutCount; i++) { - if (m_layoutArray[i]->IsBlockLayout() && (m_layoutArray[i]->GetSize() == blockSize)) + if (m_layoutArray[i]->IsCustomLayout() && + CustomLayoutKey::Equals(key, CustomLayoutKey(m_layoutArray[i]))) { return i; } @@ -175,21 +235,16 @@ class ClassLayoutTable else { unsigned index; - if (m_blkLayoutMap->Lookup(blockSize, &index)) + if (m_customLayoutMap->Lookup(key, &index)) { return index; } } - return AddBlkLayout(compiler, CreateBlkLayout(compiler, blockSize)); + return AddCustomLayout(compiler, ClassLayout::Create(compiler, builder)); } - ClassLayout* CreateBlkLayout(Compiler* compiler, unsigned blockSize) - { - return new (compiler, CMK_ClassLayout) ClassLayout(blockSize); - } - - unsigned AddBlkLayout(Compiler* compiler, ClassLayout* layout) + unsigned AddCustomLayout(Compiler* compiler, ClassLayout* layout) { if (m_layoutCount < ArrLen(m_layoutArray)) { @@ -198,7 +253,7 @@ class ClassLayoutTable } unsigned index = AddLayoutLarge(compiler, layout); - m_blkLayoutMap->Set(layout->GetSize(), index); + m_customLayoutMap->Set(CustomLayoutKey(layout), index); return index; } @@ -225,12 +280,7 @@ class ClassLayoutTable } } - return AddObjLayout(compiler, CreateObjLayout(compiler, classHandle)); - } - - ClassLayout* CreateObjLayout(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle) - { - return ClassLayout::Create(compiler, classHandle); + return AddObjLayout(compiler, ClassLayout::Create(compiler, classHandle)); } unsigned AddObjLayout(Compiler* compiler, ClassLayout* layout) @@ -256,17 +306,17 @@ class ClassLayoutTable if (m_layoutCount <= ArrLen(m_layoutArray)) { - BlkLayoutIndexMap* blkLayoutMap = new (alloc) BlkLayoutIndexMap(alloc); - ObjLayoutIndexMap* objLayoutMap = new (alloc) ObjLayoutIndexMap(alloc); + CustomLayoutIndexMap* customLayoutMap = new (alloc) CustomLayoutIndexMap(alloc); + ObjLayoutIndexMap* objLayoutMap = new (alloc) ObjLayoutIndexMap(alloc); for (unsigned i = 0; i < m_layoutCount; i++) { ClassLayout* l = m_layoutArray[i]; newArray[i] = l; - if (l->IsBlockLayout()) + if (l->IsCustomLayout()) { - blkLayoutMap->Set(l->GetSize(), i); + customLayoutMap->Set(CustomLayoutKey(l), i); } else { @@ -274,8 +324,8 @@ class ClassLayoutTable } } - m_blkLayoutMap = blkLayoutMap; - m_objLayoutMap = objLayoutMap; + m_customLayoutMap = customLayoutMap; + m_objLayoutMap = objLayoutMap; } else { @@ -334,26 +384,46 @@ unsigned Compiler::typGetLayoutNum(ClassLayout* layout) return typGetClassLayoutTable()->GetLayoutNum(layout); } -unsigned Compiler::typGetBlkLayoutNum(unsigned blockSize) +unsigned Compiler::typGetObjLayoutNum(CORINFO_CLASS_HANDLE classHandle) { - return typGetClassLayoutTable()->GetBlkLayoutNum(this, blockSize); + return typGetClassLayoutTable()->GetObjLayoutNum(this, classHandle); } -ClassLayout* Compiler::typGetBlkLayout(unsigned blockSize) +ClassLayout* Compiler::typGetObjLayout(CORINFO_CLASS_HANDLE classHandle) { - return typGetClassLayoutTable()->GetBlkLayout(this, blockSize); + return typGetClassLayoutTable()->GetObjLayout(this, classHandle); } -unsigned Compiler::typGetObjLayoutNum(CORINFO_CLASS_HANDLE classHandle) +unsigned Compiler::typGetCustomLayoutNum(const ClassLayoutBuilder& builder) { - return typGetClassLayoutTable()->GetObjLayoutNum(this, classHandle); + return typGetClassLayoutTable()->GetCustomLayoutNum(this, builder); } -ClassLayout* Compiler::typGetObjLayout(CORINFO_CLASS_HANDLE classHandle) +ClassLayout* Compiler::typGetCustomLayout(const ClassLayoutBuilder& builder) { - return typGetClassLayoutTable()->GetObjLayout(this, classHandle); + return typGetClassLayoutTable()->GetCustomLayout(this, builder); } +unsigned Compiler::typGetBlkLayoutNum(unsigned blockSize) +{ + return typGetCustomLayoutNum(ClassLayoutBuilder(this, blockSize)); +} + +ClassLayout* Compiler::typGetBlkLayout(unsigned blockSize) +{ + return typGetCustomLayout(ClassLayoutBuilder(this, blockSize)); +} + +//------------------------------------------------------------------------ +// Create: Create a ClassLayout from an EE side class handle. +// +// Parameters: +// compiler - The Compiler object +// classHandle - The class handle +// +// Return value: +// New layout representing an EE side class. +// ClassLayout* ClassLayout::Create(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle) { bool isValueClass = compiler->eeIsValueClass(classHandle); @@ -375,49 +445,82 @@ ClassLayout* ClassLayout::Create(Compiler* compiler, CORINFO_CLASS_HANDLE classH ClassLayout* layout = new (compiler, CMK_ClassLayout) ClassLayout(classHandle, isValueClass, size, type DEBUGARG(className) DEBUGARG(shortClassName)); - layout->InitializeGCPtrs(compiler); - return layout; -} - -void ClassLayout::InitializeGCPtrs(Compiler* compiler) -{ - assert(!m_gcPtrsInitialized); - assert(!IsBlockLayout()); - - if (m_size < TARGET_POINTER_SIZE) + if (layout->m_size < TARGET_POINTER_SIZE) { - assert(GetSlotCount() == 1); - assert(m_gcPtrCount == 0); + assert(layout->GetSlotCount() == 1); + assert(layout->m_gcPtrCount == 0); - m_gcPtrsArray[0] = TYPE_GC_NONE; + layout->m_gcPtrsArray[0] = TYPE_GC_NONE; } else { BYTE* gcPtrs; - - if (GetSlotCount() > sizeof(m_gcPtrsArray)) + if (layout->GetSlotCount() <= sizeof(m_gcPtrsArray)) { - gcPtrs = m_gcPtrs = new (compiler, CMK_ClassLayout) BYTE[GetSlotCount()]; + gcPtrs = layout->m_gcPtrsArray; } else { - gcPtrs = m_gcPtrsArray; + layout->m_gcPtrs = gcPtrs = new (compiler, CMK_ClassLayout) BYTE[layout->GetSlotCount()]; } - unsigned gcPtrCount = compiler->info.compCompHnd->getClassGClayout(m_classHandle, gcPtrs); + unsigned gcPtrCount = compiler->info.compCompHnd->getClassGClayout(classHandle, gcPtrs); - assert((gcPtrCount == 0) || ((compiler->info.compCompHnd->getClassAttribs(m_classHandle) & + assert((gcPtrCount == 0) || ((compiler->info.compCompHnd->getClassAttribs(classHandle) & (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_BYREF_LIKE)) != 0)); // Since class size is unsigned there's no way we could have more than 2^30 slots // so it should be safe to fit this into a 30 bits bit field. assert(gcPtrCount < (1 << 30)); - m_gcPtrCount = gcPtrCount; + layout->m_gcPtrCount = gcPtrCount; } - INDEBUG(m_gcPtrsInitialized = true;) + return layout; +} + +//------------------------------------------------------------------------ +// Create: Create a ClassLayout from a ClassLayoutBuilder. +// +// Parameters: +// compiler - The Compiler object +// builder - Builder representing the layout +// +// Return value: +// New layout representing a custom (JIT internal) class layout. +// +ClassLayout* ClassLayout::Create(Compiler* compiler, const ClassLayoutBuilder& builder) +{ + ClassLayout* newLayout = new (compiler, CMK_ClassLayout) ClassLayout(builder.m_size); + newLayout->m_gcPtrCount = builder.m_gcPtrCount; + +#ifdef DEBUG + newLayout->m_name = builder.m_name; + newLayout->m_shortName = builder.m_shortName; +#endif + + if (newLayout->GetSlotCount() <= sizeof(newLayout->m_gcPtrsArray)) + { + if (builder.m_gcPtrCount > 0) + { + memcpy(newLayout->m_gcPtrsArray, builder.m_gcPtrs, newLayout->GetSlotCount()); + } + else + { + memset(newLayout->m_gcPtrsArray, TYPE_GC_NONE, newLayout->GetSlotCount()); + } + } + else if (builder.m_gcPtrCount > 0) + { + newLayout->m_gcPtrs = builder.m_gcPtrs; + } + else + { + newLayout->m_gcPtrs = new (compiler, CMK_ClassLayout) BYTE[newLayout->GetSlotCount()]{}; + } + + return newLayout; } //------------------------------------------------------------------------ @@ -497,9 +600,23 @@ bool ClassLayout::AreCompatible(const ClassLayout* layout1, const ClassLayout* l CORINFO_CLASS_HANDLE clsHnd1 = layout1->GetClassHandle(); CORINFO_CLASS_HANDLE clsHnd2 = layout2->GetClassHandle(); - if ((clsHnd1 != NO_CLASS_HANDLE) && (clsHnd1 == clsHnd2)) + if ((clsHnd1 != NO_CLASS_HANDLE) == (clsHnd2 != NO_CLASS_HANDLE)) { - return true; + // Either both are class-based layout or both are custom layouts. + // Custom layouts only match each other if they are the same pointer. + if (clsHnd1 == NO_CLASS_HANDLE) + { + return layout1 == layout2; + } + + // For class-based layouts they are definitely compatible for the same + // handle + if (clsHnd1 == clsHnd2) + { + return true; + } + + // But they may still be compatible for different handles. } if (layout1->GetSize() != layout2->GetSize()) @@ -543,3 +660,132 @@ bool ClassLayout::AreCompatible(const ClassLayout* layout1, const ClassLayout* l } return true; } + +//------------------------------------------------------------------------ +// ClassLayoutbuilder: Construct a new builder for a class layout of the +// specified size. +// +// Arguments: +// compiler - Compiler instance +// size - Size of the layout +// +ClassLayoutBuilder::ClassLayoutBuilder(Compiler* compiler, unsigned size) + : m_compiler(compiler) + , m_size(size) +{ +} + +//------------------------------------------------------------------------ +// GetOrCreateGCPtrs: Get or create the array indicating GC pointer types. +// +// Returns: +// The array of CorInfoGCType. +// +BYTE* ClassLayoutBuilder::GetOrCreateGCPtrs() +{ + assert(m_size % TARGET_POINTER_SIZE == 0); + + if (m_gcPtrs == nullptr) + { + m_gcPtrs = new (m_compiler, CMK_ClassLayout) BYTE[m_size / TARGET_POINTER_SIZE]{}; + } + + return m_gcPtrs; +} + +//------------------------------------------------------------------------ +// SetGCPtr: Set a slot to have specified GC pointer type. +// +// Arguments: +// slot - The GC pointer slot. The slot number corresponds to offset slot * TARGET_POINTER_SIZE. +// type - Type of GC pointer that this slot contains. +// +// Remarks: +// GC pointer information can only be set in layouts of size divisible by +// TARGET_POINTER_SIZE. +// +void ClassLayoutBuilder::SetGCPtr(unsigned slot, CorInfoGCType type) +{ + BYTE* ptrs = GetOrCreateGCPtrs(); + + assert(slot * TARGET_POINTER_SIZE < m_size); + + if (ptrs[slot] != TYPE_GC_NONE) + { + m_gcPtrCount--; + } + + ptrs[slot] = static_cast(type); + + if (type != TYPE_GC_NONE) + { + m_gcPtrCount++; + } +} + +//------------------------------------------------------------------------ +// SetGCPtrType: Set a slot to have specified type. +// +// Arguments: +// slot - The GC pointer slot. The slot number corresponds to offset slot * TARGET_POINTER_SIZE. +// type - Type that this slot contains. Must be TYP_REF, TYP_BYREF or TYP_I_IMPL. +// +// Remarks: +// GC pointer information can only be set in layouts of size divisible by +// TARGET_POINTER_SIZE. +// +void ClassLayoutBuilder::SetGCPtrType(unsigned slot, var_types type) +{ + switch (type) + { + case TYP_REF: + SetGCPtr(slot, TYPE_GC_REF); + break; + case TYP_BYREF: + SetGCPtr(slot, TYPE_GC_BYREF); + break; + case TYP_I_IMPL: + SetGCPtr(slot, TYPE_GC_NONE); + break; + default: + assert(!"Invalid type passed to ClassLayoutBuilder::SetGCPtrType"); + break; + } +} + +//------------------------------------------------------------------------ +// CopyInfoFrom: Copy GC pointers from another layout. +// +// Arguments: +// offset - Offset in this builder to start copy information into. +// layout - Layout to get information from. +// +void ClassLayoutBuilder::CopyInfoFrom(unsigned offset, ClassLayout* layout) +{ + assert(offset + layout->GetSize() <= m_size); + + if (layout->GetGCPtrCount() > 0) + { + assert(offset % TARGET_POINTER_SIZE == 0); + unsigned startSlot = offset / TARGET_POINTER_SIZE; + for (unsigned slot = 0; slot < layout->GetSlotCount(); slot++) + { + SetGCPtr(startSlot + slot, layout->GetGCPtr(slot)); + } + } +} + +#ifdef DEBUG +//------------------------------------------------------------------------ +// SetName: Set the long and short name of the layout. +// +// Arguments: +// name - The long name +// shortName - The short name +// +void ClassLayoutBuilder::SetName(const char* name, const char* shortName) +{ + m_name = name; + m_shortName = shortName; +} +#endif diff --git a/src/coreclr/jit/layout.h b/src/coreclr/jit/layout.h index c2c901d1f33c9a..97122ced8d5476 100644 --- a/src/coreclr/jit/layout.h +++ b/src/coreclr/jit/layout.h @@ -6,13 +6,46 @@ #include "jit.h" +// Builder for class layouts +// +class ClassLayoutBuilder +{ + friend class ClassLayout; + friend class ClassLayoutTable; + friend struct CustomLayoutKey; + + Compiler* m_compiler; + BYTE* m_gcPtrs = nullptr; + unsigned m_size; + unsigned m_gcPtrCount = 0; +#ifdef DEBUG + const char* m_name = "UNNAMED"; + const char* m_shortName = "UNNAMED"; +#endif + + BYTE* GetOrCreateGCPtrs(); + void SetGCPtr(unsigned slot, CorInfoGCType type); +public: + // Create a class layout builder. + // + ClassLayoutBuilder(Compiler* compiler, unsigned size); + + void SetGCPtrType(unsigned slot, var_types type); + void CopyInfoFrom(unsigned offset, ClassLayout* layout); + +#ifdef DEBUG + void SetName(const char* name, const char* shortName); +#endif +}; + // Encapsulates layout information about a class (typically a value class but this can also be // be used for reference classes when they are stack allocated). The class handle is optional, -// allowing the creation of "block" layout objects having a specific size but lacking any other -// layout information. The JIT uses such layout objects in cases where a class handle is not -// available (cpblk/initblk operations) or not necessary (classes that do not contain GC pointers). +// allowing the creation of custom layout objects having a specific size where the offsets of +// GC fields can be specified during creation. class ClassLayout { +private: + // Class handle or NO_CLASS_HANDLE for "block" layouts. const CORINFO_CLASS_HANDLE m_classHandle; @@ -22,7 +55,6 @@ class ClassLayout const unsigned m_size; const unsigned m_isValueClass : 1; - INDEBUG(unsigned m_gcPtrsInitialized : 1;) // The number of GC pointers in this layout. Since the maximum size is 2^32-1 the count // can fit in at most 30 bits. unsigned m_gcPtrCount : 30; @@ -39,33 +71,33 @@ class ClassLayout // The normalized type to use in IR for block nodes with this layout. const var_types m_type; - // Class name as reported by ICorJitInfo::getClassName - INDEBUG(const char* m_className;) + // Name of the layout + INDEBUG(const char* m_name;) - // Shortened class name as constructed by Compiler::eeGetShortClassName() - INDEBUG(const char* m_shortClassName;) + // Short name of the layout + INDEBUG(const char* m_shortName;) // ClassLayout instances should only be obtained via ClassLayoutTable. friend class ClassLayoutTable; + friend class ClassLayoutBuilder; + friend struct CustomLayoutKey; ClassLayout(unsigned size) : m_classHandle(NO_CLASS_HANDLE) , m_size(size) , m_isValueClass(false) -#ifdef DEBUG - , m_gcPtrsInitialized(true) -#endif , m_gcPtrCount(0) , m_gcPtrs(nullptr) , m_type(TYP_STRUCT) #ifdef DEBUG - , m_className("block") - , m_shortClassName("block") + , m_name(size == 0 ? "Empty" : "Custom") + , m_shortName(size == 0 ? "Empty" : "Custom") #endif { } static ClassLayout* Create(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle); + static ClassLayout* Create(Compiler* compiler, const ClassLayoutBuilder& builder); ClassLayout(CORINFO_CLASS_HANDLE classHandle, bool isValueClass, @@ -74,43 +106,43 @@ class ClassLayout : m_classHandle(classHandle) , m_size(size) , m_isValueClass(isValueClass) -#ifdef DEBUG - , m_gcPtrsInitialized(false) -#endif , m_gcPtrCount(0) , m_gcPtrs(nullptr) , m_type(type) #ifdef DEBUG - , m_className(className) - , m_shortClassName(shortClassName) + , m_name(className) + , m_shortName(shortClassName) #endif { assert(size != 0); } - - void InitializeGCPtrs(Compiler* compiler); - public: + CORINFO_CLASS_HANDLE GetClassHandle() const { return m_classHandle; } - bool IsBlockLayout() const + bool IsCustomLayout() const { return m_classHandle == NO_CLASS_HANDLE; } + bool IsBlockLayout() const + { + return IsCustomLayout() && !HasGCPtr(); + } + #ifdef DEBUG const char* GetClassName() const { - return m_className; + return m_name; } const char* GetShortClassName() const { - return m_shortClassName; + return m_shortName; } #endif // DEBUG @@ -134,7 +166,7 @@ class ClassLayout // GetRegisterType: Determine register type for the layout. // // Return Value: - // TYP_UNDEF if the layout is enregistrable, register type otherwise. + // TYP_UNDEF if the layout is not enregistrable, register type otherwise. // var_types GetRegisterType() const { @@ -173,15 +205,11 @@ class ClassLayout unsigned GetGCPtrCount() const { - assert(m_gcPtrsInitialized); - return m_gcPtrCount; } bool HasGCPtr() const { - assert(m_gcPtrsInitialized); - return m_gcPtrCount != 0; } @@ -224,15 +252,11 @@ class ClassLayout private: const BYTE* GetGCPtrs() const { - assert(m_gcPtrsInitialized); - assert(!IsBlockLayout()); - return (GetSlotCount() > sizeof(m_gcPtrsArray)) ? m_gcPtrs : m_gcPtrsArray; } CorInfoGCType GetGCPtr(unsigned slot) const { - assert(m_gcPtrsInitialized); assert(slot < GetSlotCount()); if (m_gcPtrCount == 0) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index dc5144b0431659..f9d84a12bc9a28 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -2497,9 +2497,9 @@ bool Compiler::StructPromotionHelper::CanPromoteStructVar(unsigned lclNum) return false; } - if (varDsc->GetLayout()->IsBlockLayout()) + if (varDsc->GetLayout()->IsCustomLayout()) { - JITDUMP(" struct promotion of V%02u is disabled because it has block layout\n", lclNum); + JITDUMP(" struct promotion of V%02u is disabled because it has custom layout\n", lclNum); return false; } @@ -2764,8 +2764,7 @@ void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum) #ifdef DEBUG if (compiler->verbose) { - printf("\nPromoting struct local V%02u (%s):", lclNum, - compiler->eeGetClassName(varDsc->GetLayout()->GetClassHandle())); + printf("\nPromoting struct local V%02u (%s):", lclNum, varDsc->GetLayout()->GetClassName()); } #endif @@ -3368,10 +3367,10 @@ void Compiler::lvaSetStruct(unsigned varNum, ClassLayout* layout, bool unsafeVal assert(ClassLayout::AreCompatible(varDsc->GetLayout(), layout)); // Inlining could replace a canon struct type with an exact one. varDsc->SetLayout(layout); - assert(layout->IsBlockLayout() || (layout->GetSize() != 0)); + assert(layout->IsCustomLayout() || (layout->GetSize() != 0)); } - if (!layout->IsBlockLayout()) + if (!layout->IsCustomLayout()) { #ifndef TARGET_64BIT bool fDoubleAlignHint = false; @@ -7643,7 +7642,7 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r else if (varTypeIsStruct(varDsc->TypeGet())) { ClassLayout* layout = varDsc->GetLayout(); - if (layout != nullptr && !layout->IsBlockLayout()) + if (layout != nullptr) { printf(" <%s>", layout->GetClassName()); } @@ -8097,8 +8096,8 @@ Compiler::fgWalkResult Compiler::lvaStressLclFldCB(GenTree** pTree, fgWalkData* return WALK_CONTINUE; } - // Can't have GC ptrs in block layouts. - if (!varTypeIsArithmetic(lclType)) + // Structs are not currently supported + if (varTypeIsStruct(lclType)) { varDsc->lvNoLclFldStress = true; return WALK_CONTINUE; @@ -8112,6 +8111,13 @@ Compiler::fgWalkResult Compiler::lvaStressLclFldCB(GenTree** pTree, fgWalkData* return WALK_CONTINUE; } + // Pinned locals would not remain pinned if we did this transformation. + if (varDsc->lvPinned) + { + varDsc->lvNoLclFldStress = true; + return WALK_CONTINUE; + } + // Weed out "small" types like TYP_BYTE as we don't mark the GT_LCL_VAR // node with the accurate small type. If we bash lvaTable[].lvType, // then there will be no indication that it was ever a small type. @@ -8134,7 +8140,7 @@ Compiler::fgWalkResult Compiler::lvaStressLclFldCB(GenTree** pTree, fgWalkData* else { // Do the morphing - noway_assert((varType == lclType) || ((varType == TYP_STRUCT) && varDsc->GetLayout()->IsBlockLayout())); + noway_assert((varType == lclType) || ((varType == TYP_STRUCT) && varDsc->GetLayout()->IsCustomLayout())); // Calculate padding unsigned padding = pComp->lvaStressLclFldPadding(lclNum); @@ -8145,17 +8151,34 @@ Compiler::fgWalkResult Compiler::lvaStressLclFldCB(GenTree** pTree, fgWalkData* padding = roundUp(padding, genTypeSize(TYP_DOUBLE)); #endif // defined(TARGET_ARMARCH) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) + // Also for GC types we need to round up + if (varTypeIsGC(varType) || ((varType == TYP_STRUCT) && varDsc->GetLayout()->HasGCPtr())) + { + padding = roundUp(padding, TARGET_POINTER_SIZE); + } + if (varType != TYP_STRUCT) { - // Change the variable to a block struct - ClassLayout* layout = - pComp->typGetBlkLayout(roundUp(padding + pComp->lvaLclSize(lclNum), TARGET_POINTER_SIZE)); - varDsc->lvType = TYP_STRUCT; + // Change the variable to a custom layout struct + unsigned size = roundUp(padding + pComp->lvaLclSize(lclNum), TARGET_POINTER_SIZE); + ClassLayoutBuilder builder(pComp, size); +#ifdef DEBUG + builder.SetName(pComp->printfAlloc("%s_%u_Stress", varTypeName(varType), size), + pComp->printfAlloc("%s_%u", varTypeName(varType), size)); +#endif + + if (varTypeIsGC(varType)) + { + builder.SetGCPtrType(padding / TARGET_POINTER_SIZE, varType); + } + + ClassLayout* layout = pComp->typGetCustomLayout(builder); + varDsc->lvType = TYP_STRUCT; varDsc->SetLayout(layout); pComp->lvaSetVarAddrExposed(lclNum DEBUGARG(AddressExposedReason::STRESS_LCL_FLD)); - JITDUMP("Converting V%02u to %u sized block with LCL_FLD at offset (padding %u)\n", lclNum, - layout->GetSize(), padding); + JITDUMP("Converting V%02u of type %s to %u sized block with LCL_FLD at offset (padding %u)\n", lclNum, + varTypeName(varType), layout->GetSize(), padding); } tree->gtFlags |= GTF_GLOB_REF; diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index c764a7259fba38..0f7397012f2d14 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -947,13 +947,16 @@ void LinearScan::setBlockSequence() bbVisitedSet = BitVecOps::MakeEmpty(traits); assert((blockSequence == nullptr) && (bbSeqCount == 0)); - FlowGraphDfsTree* const dfsTree = compiler->fgComputeDfs(); + + compiler->m_dfsTree = compiler->fgComputeDfs(); + FlowGraphDfsTree* const dfsTree = compiler->m_dfsTree; blockSequence = new (compiler, CMK_LSRA) BasicBlock*[compiler->fgBBcount]; if (compiler->opts.OptimizationEnabled() && dfsTree->HasCycle()) { - // Ensure loop bodies are compact in the visitation order - FlowGraphNaturalLoops* const loops = FlowGraphNaturalLoops::Find(dfsTree); + // Ensure loop bodies are compact in the visitation order. + compiler->m_loops = FlowGraphNaturalLoops::Find(dfsTree); + FlowGraphNaturalLoops* const loops = compiler->m_loops; unsigned index = 0; auto addToSequence = [this, &index](BasicBlock* block) { @@ -1319,6 +1322,7 @@ PhaseStatus LinearScan::doLinearScan() { assert(compiler->fgBBcount > bbSeqCount); compiler->fgBBs = nullptr; + compiler->fgInvalidateDfsTree(); } return PhaseStatus::MODIFIED_EVERYTHING; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 4c0fc233196482..fb362d51b381b9 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -3296,7 +3296,7 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg) found = ForEachHbvBitSet(*fgAvailableOutgoingArgTemps, [&](indexType lclNum) { LclVarDsc* varDsc = lvaGetDesc((unsigned)lclNum); ClassLayout* layout = varDsc->GetLayout(); - if (!layout->IsBlockLayout() && (layout->GetClassHandle() == copyBlkClass)) + if (!layout->IsCustomLayout() && (layout->GetClassHandle() == copyBlkClass)) { tmp = (unsigned)lclNum; JITDUMP("reusing outgoing struct arg V%02u\n", tmp); diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 8c84d47cf75536..ec3b37eb15901e 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -1840,7 +1840,7 @@ GenTree* ObjectAllocator::IsGuard(BasicBlock* block, GuardInfo* info) info->m_local = addr->AsLclVar()->GetLclNum(); bool isNonNull = false; bool isExact = false; - info->m_type = (CORINFO_CLASS_HANDLE)op2->AsIntCon()->gtIconVal; + info->m_type = (CORINFO_CLASS_HANDLE)op2->AsIntCon()->gtCompileTimeHandle; JITDUMP("... " FMT_BB " is guard for V%02u\n", block->bbNum, info->m_local); return tree; @@ -2347,10 +2347,10 @@ bool ObjectAllocator::CheckCanClone(CloneInfo* info) // object in the blocks we intend to clone (and beyond). Verify those have the // expected def-use behavior. // - // The goal of all this is to try and ensure that if we rewrite all the T,V,U appeances + // The goal of all this is to try and ensure that if we rewrite all the T,V,U appearances // to new locals in the cloned code we get proper behavior. // - // There is one distingushed local V (info.m_local) that holds the result of the + // There is one distinguished local V (info.m_local) that holds the result of the // initial GDV and is the local tested in subsequent GDVs. It must have a single def. // // The other locals are either temps T that refer to the allocated object between @@ -2373,7 +2373,7 @@ bool ObjectAllocator::CheckCanClone(CloneInfo* info) // Tv's use should be at the def of V. // // For the U's: all Ui appearances should be dominated by the def of V; all Ui defs - // should have another Ui or V as their source. (We should also verfy each Ui is + // should have another Ui or V as their source. (We should also verify each Ui is // single-def and the def dominates all the Ui uses, but this may not work out...?) // // Also we do not expect any Ti or Ui use to be a GDV guard. U's typically arise from @@ -2422,45 +2422,50 @@ bool ObjectAllocator::CheckCanClone(CloneInfo* info) BitVecTraits traits(comp->compBasicBlockID, comp); BitVec visitedBlocks(BitVecOps::MakeEmpty(&traits)); toVisit.Push(allocBlock); + BitVecOps::AddElemD(&traits, visitedBlocks, allocBlock->bbID); - // todo -- some kind of runaway size limit + // We don't expect to have to search very far // + unsigned searchCount = 0; + unsigned const searchLimit = 25; + while (toVisit.Height() > 0) { - BasicBlock* const visitBlock = toVisit.Pop(); - if (!BitVecOps::TryAddElemD(&traits, visitedBlocks, visitBlock->bbID)) + BasicBlock* const block = toVisit.Pop(); + + if (searchCount > searchLimit) { - continue; + JITDUMP("Too many blocks between alloc and def block\n"); + return false; } - if (visitBlock != allocBlock) + if (block != allocBlock) { - visited->push_back(visitBlock); + visited->push_back(block); } // We expect this stretch of blocks to all be in the same EH region. // - if (!BasicBlock::sameEHRegion(allocBlock, visitBlock)) + if (!BasicBlock::sameEHRegion(allocBlock, block)) { - JITDUMP("Unexpected: new EH region at " FMT_BB "\n", visitBlock->bbNum); + JITDUMP("Unexpected: new EH region at " FMT_BB "\n", block->bbNum); return false; } - if (visitBlock == defBlock) + if (block == defBlock) { continue; } - JITDUMP("walking through " FMT_BB "\n", visitBlock->bbNum); + JITDUMP("walking through " FMT_BB "\n", block->bbNum); - for (BasicBlock* const succ : visitBlock->Succs()) - { - if (BitVecOps::IsMember(&traits, visitedBlocks, succ->bbID)) + block->VisitRegularSuccs(comp, [&](BasicBlock* succ) { + if (BitVecOps::TryAddElemD(&traits, visitedBlocks, succ->bbID)) { - continue; + toVisit.Push(succ); } - toVisit.Push(succ); - } + return BasicBlockVisit::Continue; + }); } JITDUMP("def block " FMT_BB " post-dominates allocation site " FMT_BB "\n", defBlock->bbNum, allocBlock->bbNum); @@ -2617,14 +2622,18 @@ bool ObjectAllocator::CheckCanClone(CloneInfo* info) for (EnumeratorVarAppearance* const a : *(ev->m_appearances)) { - if (!comp->m_domTree->Dominates(defBlock, a->m_block)) + BasicBlock* const aBlock = a->m_block; + if (!comp->m_domTree->Dominates(defBlock, aBlock)) { JITDUMP("%sV%02u %s in " FMT_BB " not dominated by def " FMT_BB "\n", ev->m_isUseTemp ? "Use temp" : "", lclNum, a->m_isDef ? "def" : "use", a->m_block->bbNum, defBlock->bbNum); return false; } - toVisit.Push(a->m_block); + if (BitVecOps::TryAddElemD(&traits, visitedBlocks, aBlock->bbID)) + { + toVisit.Push(aBlock); + } } } @@ -2637,23 +2646,19 @@ bool ObjectAllocator::CheckCanClone(CloneInfo* info) // while (toVisit.Height() > 0) { - BasicBlock* const visitBlock = toVisit.Pop(); - if (!BitVecOps::TryAddElemD(&traits, visitedBlocks, visitBlock->bbID)) - { - continue; - } - visited->push_back(visitBlock); + BasicBlock* const block = toVisit.Pop(); + visited->push_back(block); // If we see try region entries here, we will handle them below. // - if (comp->bbIsTryBeg(visitBlock)) + if (comp->bbIsTryBeg(block)) { - toVisitTryEntry->push_back(visitBlock); + toVisitTryEntry->push_back(block); } - JITDUMP("walking back through " FMT_BB "\n", visitBlock->bbNum); + JITDUMP("walking back through " FMT_BB "\n", block->bbNum); - for (FlowEdge* predEdge = comp->BlockPredsWithEH(visitBlock); predEdge != nullptr; + for (FlowEdge* predEdge = comp->BlockPredsWithEH(block); predEdge != nullptr; predEdge = predEdge->getNextPredEdge()) { BasicBlock* const predBlock = predEdge->getSourceBlock(); @@ -2662,11 +2667,10 @@ bool ObjectAllocator::CheckCanClone(CloneInfo* info) // (consider eh paths?) // assert(comp->m_domTree->Dominates(defBlock, predBlock)); - if (BitVecOps::IsMember(&traits, visitedBlocks, predBlock->bbID)) + if (BitVecOps::TryAddElemD(&traits, visitedBlocks, predBlock->bbID)) { - continue; + toVisit.Push(predBlock); } - toVisit.Push(predBlock); } } @@ -2754,8 +2758,6 @@ bool ObjectAllocator::CheckCanClone(CloneInfo* info) // Save off blocks that we need to clone // - // TODO: use postorder nums to keeping the vector and bitvec? - // info->m_blocksToClone = visited; info->m_blocks = visitedBlocks; info->m_canClone = true; diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets index c2627502f9ddd6..059d1754c68263 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets @@ -24,6 +24,8 @@ <_hostPackageName Condition="'$(_targetsNonPortableSdkRid)' == 'true'">runtime.$(RuntimeIdentifier).Microsoft.DotNet.ILCompiler <_targetPackageName>runtime.$(_originalTargetOS)-$(_targetArchitecture).Microsoft.DotNet.ILCompiler <_targetPackageName Condition="'$(_targetsNonPortableSdkRid)' == 'true'">runtime.$(RuntimeIdentifier).Microsoft.DotNet.ILCompiler + <_targetRuntimePackName>Microsoft.NETCore.App.Runtime.NativeAOT.$(_originalTargetOS)-$(_targetArchitecture) + <_targetRuntimePackName Condition="'$(_targetsNonPortableSdkRid)' == 'true'">Microsoft.NETCore.App.Runtime.NativeAOT.$(RuntimeIdentifier) <_targetOS>$(_originalTargetOS) @@ -64,8 +66,10 @@ @(ResolvedILCompilerPack->'%(PackageDirectory)') - @(ResolvedTargetILCompilerPack->'%(PackageDirectory)') @(ResolvedILCompilerPack->'%(PackageDirectory)') + @(ResolvedTargetILCompilerPack->'%(PackageDirectory)') + true + $(PublishAotUsingRuntimePack) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets index 0e633f34e3b24f..656e525ca32eb7 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets @@ -68,8 +68,8 @@ Text="Add a PackageReference for '$(_hostPackageName)' to allow cross-compilation for $(_targetArchitecture)" /> - - + + + + + <_NETCoreAppRuntimePackPath Condition="'$(PublishAotUsingRuntimePack)' == 'true'">%(_NETCoreAppFrameworkReference.RuntimePackPath)/runtimes/$(RuntimeIdentifier)/ + <_NETCoreAppRuntimePackPath Condition="'$(PublishAotUsingRuntimePack)' != 'true'">$(RuntimePackagePath)/runtimes/$(RuntimeIdentifier)/ + $(_NETCoreAppRuntimePackPath)\native\ + $(IlcFrameworkNativePath) + + + + $(RuntimePackagePath)\framework\ + $(RuntimePackagePath)\framework\ + $(RuntimePackagePath)\sdk\ + + $(IlcHostPackagePath)\tools\ - $(RuntimePackagePath)\framework\ - <_NETCoreAppRuntimePackPath>%(_NETCoreAppFrameworkReference.RuntimePackPath)/runtimes/$(RuntimeIdentifier)/ - $(_NETCoreAppRuntimePackPath)\native\ - $(RuntimePackagePath)\framework\ - $(IlcFrameworkNativePath) - $(RuntimePackagePath)\sdk\ $(RuntimePackagePath)\mibc\ - + - + diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs index bab6cd0e8121df..5f05fdeb9f4703 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs @@ -277,22 +277,22 @@ private static bool IsInterfaceImplementedOnType(MetadataType type, MetadataType private static MethodDesc FindImplFromDeclFromMethodImpls(MetadataType type, MethodDesc decl) { + if (decl.OwningType.IsInterface) + return FindInterfaceImplFromDeclFromMethodImpls(type, decl); + MethodImplRecord[] foundMethodImpls = type.FindMethodsImplWithMatchingDeclName(decl.Name); if (foundMethodImpls == null) return null; - bool interfaceDecl = decl.OwningType.IsInterface; - foreach (MethodImplRecord record in foundMethodImpls) { MethodDesc recordDecl = record.Decl; - if (interfaceDecl != recordDecl.OwningType.IsInterface) + if (recordDecl.OwningType.IsInterface) continue; - if (!interfaceDecl) - recordDecl = FindSlotDefiningMethodForVirtualMethod(recordDecl); + recordDecl = FindSlotDefiningMethodForVirtualMethod(recordDecl); if (recordDecl == decl) { @@ -303,6 +303,56 @@ private static MethodDesc FindImplFromDeclFromMethodImpls(MetadataType type, Met return null; } + private static MethodDesc FindInterfaceImplFromDeclFromMethodImpls(MetadataType type, MethodDesc decl) + { + Debug.Assert(decl.OwningType.IsInterface); + + MethodImplRecord[] foundMethodImpls = type.FindMethodsImplWithMatchingDeclName(decl.Name); + + if (foundMethodImpls == null) + return null; + + // We might find more than one result due to generic parameter folding + var results = new ArrayBuilder(1); + for (int i = 0; i < foundMethodImpls.Length; i++) + { + MethodDesc recordDecl = foundMethodImpls[i].Decl; + if (recordDecl == decl) + { + results.Add(i); + } + } + + if (results.Count == 0) + return null; + + int resultIndex = results[0]; + + // If we found multiple MethodImpls, need to do a tie break using type declaration order + if (results.Count > 1) + { + MetadataType typeDefinition = (MetadataType)type.GetTypeDefinition(); + DefType[] interfacesOnDefinition = typeDefinition.RuntimeInterfaces; + MethodImplRecord[] foundMethodImplsOnDefinition = typeDefinition.FindMethodsImplWithMatchingDeclName(decl.Name); + Debug.Assert(foundMethodImplsOnDefinition.Length == foundMethodImpls.Length); + + int bestInterfaceIndex = int.MaxValue; + + for (int i = 0; i < results.Count; i++) + { + int index = Array.IndexOf(interfacesOnDefinition, foundMethodImplsOnDefinition[results[i]].Decl.OwningType); + Debug.Assert(index >= 0); + if (index < bestInterfaceIndex) + { + bestInterfaceIndex = index; + resultIndex = i; + } + } + } + + return FindSlotDefiningMethodForVirtualMethod(foundMethodImpls[resultIndex].Body); + } + private static bool IsInterfaceExplicitlyImplementedOnType(MetadataType type, MetadataType interfaceType) { foreach (TypeDesc iface in type.ExplicitlyImplementedInterfaces) diff --git a/src/coreclr/vm/amd64/AsmHelpers.asm b/src/coreclr/vm/amd64/AsmHelpers.asm index 95714fd0757321..380893996db617 100644 --- a/src/coreclr/vm/amd64/AsmHelpers.asm +++ b/src/coreclr/vm/amd64/AsmHelpers.asm @@ -447,6 +447,25 @@ NESTED_ENTRY OnCallCountThresholdReachedStub, _TEXT TAILJMP_RAX NESTED_END OnCallCountThresholdReachedStub, _TEXT +extern JIT_PatchpointWorkerWorkerWithPolicy:proc + +NESTED_ENTRY JIT_Patchpoint, _TEXT + PROLOG_WITH_TRANSITION_BLOCK + + lea rcx, [rsp + __PWTB_TransitionBlock] ; TransitionBlock * + call JIT_PatchpointWorkerWorkerWithPolicy + + EPILOG_WITH_TRANSITION_BLOCK_RETURN + TAILJMP_RAX +NESTED_END JIT_Patchpoint, _TEXT + +; first arg register holds iloffset, which needs to be moved to the second register, and the first register filled with NULL +LEAF_ENTRY JIT_PartialCompilationPatchpoint, _TEXT + mov rdx, rcx + xor rcx, rcx + jmp JIT_Patchpoint +LEAF_END JIT_PartialCompilationPatchpoint, _TEXT + endif ; FEATURE_TIERED_COMPILATION LEAF_ENTRY JIT_PollGC, _TEXT diff --git a/src/coreclr/vm/amd64/unixasmhelpers.S b/src/coreclr/vm/amd64/unixasmhelpers.S index 7e404a2a09cf6c..10ab11933caee4 100644 --- a/src/coreclr/vm/amd64/unixasmhelpers.S +++ b/src/coreclr/vm/amd64/unixasmhelpers.S @@ -195,4 +195,20 @@ NESTED_ENTRY OnCallCountThresholdReachedStub, _TEXT, NoHandler TAILJMP_RAX NESTED_END OnCallCountThresholdReachedStub, _TEXT +NESTED_ENTRY JIT_Patchpoint, _TEXT, NoHandler + PROLOG_WITH_TRANSITION_BLOCK + + lea rdi, [rsp + __PWTB_TransitionBlock] // TransitionBlock * + call C_FUNC(JIT_PatchpointWorkerWorkerWithPolicy) + + EPILOG_WITH_TRANSITION_BLOCK_RETURN +NESTED_END JIT_Patchpoint, _TEXT + +// first arg register holds iloffset, which needs to be moved to the second register, and the first register filled with NULL +LEAF_ENTRY JIT_PartialCompilationPatchpoint, _TEXT + mov rsi, rdi + xor rdi, rdi + jmp C_FUNC(JIT_Patchpoint) +LEAF_END JIT_PartialCompilationPatchpoint, _TEXT + #endif // FEATURE_TIERED_COMPILATION diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index a6801090ad9b25..fdfa833d250f7b 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -747,6 +747,22 @@ NESTED_ENTRY OnCallCountThresholdReachedStub, _TEXT, NoHandler EPILOG_BRANCH_REG x9 NESTED_END OnCallCountThresholdReachedStub, _TEXT +NESTED_ENTRY JIT_Patchpoint, _TEXT, NoHandler + PROLOG_WITH_TRANSITION_BLOCK + + add x0, sp, #__PWTB_TransitionBlock // TransitionBlock * + bl C_FUNC(JIT_PatchpointWorkerWorkerWithPolicy) + + EPILOG_WITH_TRANSITION_BLOCK_RETURN +NESTED_END JIT_Patchpoint, _TEXT + +// first arg register holds iloffset, which needs to be moved to the second register, and the first register filled with NULL +LEAF_ENTRY JIT_PartialCompilationPatchpoint, _TEXT + mov x1, x0 + mov x0, #0 + b C_FUNC(JIT_Patchpoint) +LEAF_END JIT_PartialCompilationPatchpoint, _TEXT + #endif // FEATURE_TIERED_COMPILATION LEAF_ENTRY JIT_ValidateIndirectCall, _TEXT diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm index e240919a7395f0..cd7c06500ee315 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -1139,6 +1139,24 @@ __HelperNakedFuncName SETS "$helper":CC:"Naked" EPILOG_BRANCH_REG x9 NESTED_END + IMPORT JIT_PatchpointWorkerWorkerWithPolicy + + NESTED_ENTRY JIT_Patchpoint + PROLOG_WITH_TRANSITION_BLOCK + + add x0, sp, #__PWTB_TransitionBlock ; TransitionBlock * + bl JIT_PatchpointWorkerWorkerWithPolicy + + EPILOG_WITH_TRANSITION_BLOCK_RETURN + NESTED_END + + // first arg register holds iloffset, which needs to be moved to the second register, and the first register filled with NULL + LEAF_ENTRY JIT_PartialCompilationPatchpoint + mov x1, x0 + mov x0, #0 + b JIT_Patchpoint + LEAF_END + #endif ; FEATURE_TIERED_COMPILATION LEAF_ENTRY JIT_ValidateIndirectCall diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index d15c724a20b5e3..41539478e78723 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -2209,4 +2209,28 @@ inline BOOL IsRetBuffPassedAsFirstArg() #endif } +inline TADDR GetFirstArgumentRegisterValuePtr(TransitionBlock * pTransitionBlock) +{ + TADDR pArgument = (TADDR)pTransitionBlock + TransitionBlock::GetOffsetOfArgumentRegisters(); +#ifdef TARGET_X86 + // x86 is special as always + pArgument += offsetof(ArgumentRegisters, ECX); +#endif + + return pArgument; +} + +inline TADDR GetSecondArgumentRegisterValuePtr(TransitionBlock * pTransitionBlock) +{ + TADDR pArgument = (TADDR)pTransitionBlock + TransitionBlock::GetOffsetOfArgumentRegisters(); +#ifdef TARGET_X86 + // x86 is special as always + pArgument += offsetof(ArgumentRegisters, EDX); +#else + pArgument += sizeof(TADDR); +#endif + + return pArgument; +} + #endif // __CALLING_CONVENTION_INCLUDED diff --git a/src/coreclr/vm/codeman.h b/src/coreclr/vm/codeman.h index 87b8bfd27f03a6..61bd8b07cdb32c 100644 --- a/src/coreclr/vm/codeman.h +++ b/src/coreclr/vm/codeman.h @@ -2477,7 +2477,7 @@ class EECodeInfo TADDR GetSavedMethodCode(); - TADDR GetStartAddress(); + TADDR GetStartAddress() const; BOOL IsValid() { @@ -2505,15 +2505,15 @@ class EECodeInfo } // This returns a pointer to the start of an instruction; conceptually, a PINSTR. - TADDR GetCodeAddress() + TADDR GetCodeAddress() const { LIMITED_METHOD_DAC_CONTRACT; return PCODEToPINSTR(m_codeAddress); } - NativeCodeVersion GetNativeCodeVersion(); + NativeCodeVersion GetNativeCodeVersion() const; - MethodDesc * GetMethodDesc() + MethodDesc * GetMethodDesc() const { LIMITED_METHOD_DAC_CONTRACT; return m_pMD; diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 630ebfe42286fd..26a5398e104738 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -2302,7 +2302,7 @@ HCIMPLEND // // Returns the address of the jitted code. // Returns NULL if osr method can't be created. -static PCODE JitPatchpointWorker(MethodDesc* pMD, EECodeInfo& codeInfo, int ilOffset) +static PCODE JitPatchpointWorker(MethodDesc* pMD, const EECodeInfo& codeInfo, int ilOffset) { STANDARD_VM_CONTRACT; PCODE osrVariant = (PCODE)NULL; @@ -2347,55 +2347,20 @@ static PCODE JitPatchpointWorker(MethodDesc* pMD, EECodeInfo& codeInfo, int ilOf return osrVariant; } -// Helper method wrapper to set up a frame so we can invoke methods that might GC -HCIMPL3(PCODE, JIT_Patchpoint_Framed, MethodDesc* pMD, EECodeInfo& codeInfo, int ilOffset) +static PCODE PatchpointOptimizationPolicy(TransitionBlock* pTransitionBlock, int* counter, int ilOffset, PerPatchpointInfo * ppInfo, const EECodeInfo& codeInfo, bool *pIsNewMethod) { - PCODE result = (PCODE)NULL; - - HELPER_METHOD_FRAME_BEGIN_RET_0(); - - GCX_PREEMP(); - result = JitPatchpointWorker(pMD, codeInfo, ilOffset); - - HELPER_METHOD_FRAME_END(); - - return result; -} -HCIMPLEND - -// Jit helper invoked at a patchpoint. -// -// Checks to see if this is a known patchpoint, if not, -// an entry is added to the patchpoint table. -// -// When the patchpoint has been hit often enough to trigger -// a transition, create an OSR method. -// -// Currently, counter is a pointer into the Tier0 method stack -// frame so we have exclusive access. - -void JIT_Patchpoint(int* counter, int ilOffset) -{ - // BEGIN_PRESERVE_LAST_ERROR; - DWORD dwLastError = ::GetLastError(); - - // This method may not return normally - STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_TRIGGERS; STATIC_CONTRACT_MODE_COOPERATIVE; - // Patchpoint identity is the helper return address - PCODE ip = (PCODE)_ReturnAddress(); + // See if we have an OSR method for this patchpoint. + PCODE osrMethodCode = ppInfo->m_osrMethodCode; + *pIsNewMethod = false; + TADDR ip = codeInfo.GetCodeAddress(); - // Fetch or setup patchpoint info for this patchpoint. - EECodeInfo codeInfo(ip); MethodDesc* pMD = codeInfo.GetMethodDesc(); - LoaderAllocator* allocator = pMD->GetLoaderAllocator(); - OnStackReplacementManager* manager = allocator->GetOnStackReplacementManager(); - PerPatchpointInfo * ppInfo = manager->GetPerPatchpointInfo(ip); - PCODE osrMethodCode = (PCODE)NULL; - bool isNewMethod = false; - // In the current prototype, counter is shared by all patchpoints + // In the current implementation, counter is shared by all patchpoints // in a method, so no matter what happens below, we don't want to // impair those other patchpoints. // @@ -2406,7 +2371,7 @@ void JIT_Patchpoint(int* counter, int ilOffset) // // So we always reset the counter to the bump value. // - // In the prototype, counter is a location in a stack frame, + // In the implementation, counter is a location in a stack frame, // so we can update it without worrying about other threads. const int counterBump = g_pConfig->OSR_CounterBump(); *counter = counterBump; @@ -2415,18 +2380,14 @@ void JIT_Patchpoint(int* counter, int ilOffset) const int ppId = ppInfo->m_patchpointId; #endif - // Is this a patchpoint that was previously marked as invalid? If so, just return to the Tier0 method. if ((ppInfo->m_flags & PerPatchpointInfo::patchpoint_invalid) == PerPatchpointInfo::patchpoint_invalid) { - LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "Jit_Patchpoint: invalid patchpoint [%d] (0x%p) in Method=0x%pM (%s::%s) at offset %d\n", + LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "PatchpointOptimizationPolicy: invalid patchpoint [%d] (0x%p) in Method=0x%pM (%s::%s) at offset %d\n", ppId, ip, pMD, pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, ilOffset)); goto DONE; } - // See if we have an OSR method for this patchpoint. - osrMethodCode = ppInfo->m_osrMethodCode; - if (osrMethodCode == (PCODE)NULL) { // No OSR method yet, let's see if we should create one. @@ -2446,7 +2407,7 @@ void JIT_Patchpoint(int* counter, int ilOffset) if ((ppId < lowId) || (ppId > highId)) { - LOG((LF_TIEREDCOMPILATION, LL_INFO10, "Jit_Patchpoint: ignoring patchpoint [%d] (0x%p) in Method=0x%pM (%s::%s) at offset %d\n", + LOG((LF_TIEREDCOMPILATION, LL_INFO10, "PatchpointOptimizationPolicy: ignoring patchpoint [%d] (0x%p) in Method=0x%pM (%s::%s) at offset %d\n", ppId, ip, pMD, pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, ilOffset)); goto DONE; } @@ -2483,7 +2444,7 @@ void JIT_Patchpoint(int* counter, int ilOffset) const int hitCount = InterlockedIncrement(&ppInfo->m_patchpointCount); const int hitLogLevel = (hitCount == 1) ? LL_INFO10 : LL_INFO1000; - LOG((LF_TIEREDCOMPILATION, hitLogLevel, "Jit_Patchpoint: patchpoint [%d] (0x%p) hit %d in Method=0x%pM (%s::%s) [il offset %d] (limit %d)\n", + LOG((LF_TIEREDCOMPILATION, hitLogLevel, "PatchpointOptimizationPolicy: patchpoint [%d] (0x%p) hit %d in Method=0x%pM (%s::%s) [il offset %d] (limit %d)\n", ppId, ip, hitCount, pMD, pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, ilOffset, hitLimit)); // Defer, if we haven't yet reached the limit @@ -2496,7 +2457,7 @@ void JIT_Patchpoint(int* counter, int ilOffset) LONG oldFlags = ppInfo->m_flags; if ((oldFlags & PerPatchpointInfo::patchpoint_triggered) == PerPatchpointInfo::patchpoint_triggered) { - LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "Jit_Patchpoint: AWAITING OSR method for patchpoint [%d] (0x%p)\n", ppId, ip)); + LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "PatchpointOptimizationPolicy: AWAITING OSR method for patchpoint [%d] (0x%p)\n", ppId, ip)); goto DONE; } @@ -2505,49 +2466,252 @@ void JIT_Patchpoint(int* counter, int ilOffset) if (!triggerTransition) { - LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "Jit_Patchpoint: (lost race) AWAITING OSR method for patchpoint [%d] (0x%p)\n", ppId, ip)); + LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "PatchpointOptimizationPolicy: (lost race) AWAITING OSR method for patchpoint [%d] (0x%p)\n", ppId, ip)); goto DONE; } - // Time to create the OSR method. - // - // We currently do this synchronously. We could instead queue - // up a request on some worker thread, like we do for - // rejitting, and return control to the Tier0 method. It may - // eventually return here, if the patchpoint is hit often - // enough. - // - // There is a chance the async version will create methods - // that are never used (just like there is a chance that Tier1 - // methods are ever called). - // - // In this prototype we want to expose bugs in the jitted code - // for OSR methods, so we stick with synchronous creation. - LOG((LF_TIEREDCOMPILATION, LL_INFO10, "Jit_Patchpoint: patchpoint [%d] (0x%p) TRIGGER at count %d\n", ppId, ip, hitCount)); + MAKE_CURRENT_THREAD_AVAILABLE(); + + #ifdef _DEBUG + Thread::ObjectRefFlush(CURRENT_THREAD); + #endif + + FrameWithCookie frame(pTransitionBlock, 0); + DynamicHelperFrame * pFrame = &frame; + + pFrame->Push(CURRENT_THREAD); + + INSTALL_MANAGED_EXCEPTION_DISPATCHER; + INSTALL_UNWIND_AND_CONTINUE_HANDLER; - // Invoke the helper to build the OSR method - osrMethodCode = HCCALL3(JIT_Patchpoint_Framed, pMD, codeInfo, ilOffset); + GCX_PREEMP(); - // If that failed, mark the patchpoint as invalid. + osrMethodCode = ppInfo->m_osrMethodCode; if (osrMethodCode == (PCODE)NULL) { - // Unexpected, but not fatal - STRESS_LOG3(LF_TIEREDCOMPILATION, LL_WARNING, "Jit_Patchpoint: patchpoint (0x%p) OSR method creation failed," - " marking patchpoint invalid for Method=0x%pM il offset %d\n", ip, pMD, ilOffset); + // Time to create the OSR method. + // + // We currently do this synchronously. We could instead queue + // up a request on some worker thread, like we do for + // rejitting, and return control to the Tier0 method. It may + // eventually return here, if the patchpoint is hit often + // enough. + // + // There is a chance the async version will create methods + // that are never used (just like there is a chance that Tier1 + // methods are ever called). + // + // We want to expose bugs in the jitted code + // for OSR methods, so we stick with synchronous creation. + LOG((LF_TIEREDCOMPILATION, LL_INFO10, "PatchpointOptimizationPolicy: patchpoint [%d] (0x%p) TRIGGER at count %d\n", ppId, ip, hitCount)); - InterlockedOr(&ppInfo->m_flags, (LONG)PerPatchpointInfo::patchpoint_invalid); - goto DONE; + // Invoke the helper to build the OSR method + osrMethodCode = JitPatchpointWorker(pMD, codeInfo, ilOffset); + + // If that failed, mark the patchpoint as invalid. + if (osrMethodCode == (PCODE)NULL) + { + // Unexpected, but not fatal + STRESS_LOG3(LF_TIEREDCOMPILATION, LL_WARNING, "PatchpointOptimizationPolicy: patchpoint (0x%p) OSR method creation failed," + " marking patchpoint invalid for Method=0x%pM il offset %d\n", ip, pMD, ilOffset); + + InterlockedOr(&ppInfo->m_flags, (LONG)PerPatchpointInfo::patchpoint_invalid); + } + else + { + *pIsNewMethod = true; + ppInfo->m_osrMethodCode = osrMethodCode; + } } - // We've successfully created the osr method; make it available. - _ASSERTE(ppInfo->m_osrMethodCode == (PCODE)NULL); - ppInfo->m_osrMethodCode = osrMethodCode; - isNewMethod = true; + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; + UNINSTALL_MANAGED_EXCEPTION_DISPATCHER; + + pFrame->Pop(CURRENT_THREAD); + } + return osrMethodCode; + +DONE: + return (PCODE)NULL; +} + +static PCODE PatchpointRequiredPolicy(TransitionBlock* pTransitionBlock, int* counter, int ilOffset, PerPatchpointInfo * ppInfo, const EECodeInfo& codeInfo, bool *pIsNewMethod) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_COOPERATIVE; + + *pIsNewMethod = false; + MethodDesc* pMD = codeInfo.GetMethodDesc(); + TADDR ip = codeInfo.GetCodeAddress(); + +#ifdef _DEBUG + const int ppId = ppInfo->m_patchpointId; +#endif + + if ((ppInfo->m_flags & PerPatchpointInfo::patchpoint_invalid) == PerPatchpointInfo::patchpoint_invalid) + { + LOG((LF_TIEREDCOMPILATION, LL_FATALERROR, "PatchpointRequiredPolicy: invalid patchpoint [%d] (0x%p) in Method=0x%pM (%s::%s) at offset %d\n", + ppId, ip, pMD, pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, ilOffset)); + EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); + } + + MAKE_CURRENT_THREAD_AVAILABLE(); + +#ifdef _DEBUG + Thread::ObjectRefFlush(CURRENT_THREAD); +#endif + + FrameWithCookie frame(pTransitionBlock, 0); + DynamicHelperFrame * pFrame = &frame; + + pFrame->Push(CURRENT_THREAD); + + INSTALL_MANAGED_EXCEPTION_DISPATCHER; + INSTALL_UNWIND_AND_CONTINUE_HANDLER; + + { + GCX_PREEMP(); + + DWORD backoffs = 0; + while (ppInfo->m_osrMethodCode == (PCODE)NULL) + { + // Invalid patchpoints are fatal, for partial compilation patchpoints + // + if ((ppInfo->m_flags & PerPatchpointInfo::patchpoint_invalid) == PerPatchpointInfo::patchpoint_invalid) + { + LOG((LF_TIEREDCOMPILATION, LL_FATALERROR, "PatchpointRequiredPolicy: invalid patchpoint [%d] (0x%p) in Method=0x%pM (%s::%s) at offset %d\n", + ppId, ip, pMD, pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, ilOffset)); + EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); + } + + // Make sure no other thread is trying to create the OSR method. + // + LONG oldFlags = ppInfo->m_flags; + if ((oldFlags & PerPatchpointInfo::patchpoint_triggered) == PerPatchpointInfo::patchpoint_triggered) + { + LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "PatchpointRequiredPolicy: AWAITING OSR method for patchpoint [%d] (0x%p)\n", ppId, ip)); + __SwitchToThread(0, backoffs++); + continue; + } + + // Make sure we win the race to create the OSR method + // + LONG newFlags = ppInfo->m_flags | PerPatchpointInfo::patchpoint_triggered; + BOOL triggerTransition = InterlockedCompareExchange(&ppInfo->m_flags, newFlags, oldFlags) == oldFlags; + + if (!triggerTransition) + { + LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "PatchpointRequiredPolicy: (lost race) AWAITING OSR method for patchpoint [%d] (0x%p)\n", ppId, ip)); + __SwitchToThread(0, backoffs++); + continue; + } + + // Invoke the helper to build the OSR method + // + // TODO: may not want to optimize this part of the method, if it's truly partial compilation + // and can't possibly rejoin into the main flow. + // + // (but consider: throw path in method with try/catch, OSR method will contain more than just the throw?) + // + LOG((LF_TIEREDCOMPILATION, LL_INFO10, "PatchpointRequiredPolicy: patchpoint [%d] (0x%p) TRIGGER\n", ppId, ip)); + PCODE newMethodCode = JitPatchpointWorker(pMD, codeInfo, ilOffset); + + // If that failed, mark the patchpoint as invalid. + // This is fatal, for partial compilation patchpoints + // + if (newMethodCode == (PCODE)NULL) + { + STRESS_LOG3(LF_TIEREDCOMPILATION, LL_WARNING, "PatchpointRequiredPolicy: patchpoint (0x%p) OSR method creation failed," + " marking patchpoint invalid for Method=0x%pM il offset %d\n", ip, pMD, ilOffset); + InterlockedOr(&ppInfo->m_flags, (LONG)PerPatchpointInfo::patchpoint_invalid); + EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); + break; + } + + // We've successfully created the osr method; make it available. + _ASSERTE(ppInfo->m_osrMethodCode == (PCODE)NULL); + ppInfo->m_osrMethodCode = newMethodCode; + *pIsNewMethod = true; + } } + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; + UNINSTALL_MANAGED_EXCEPTION_DISPATCHER; + + pFrame->Pop(CURRENT_THREAD); + // If we get here, we have code to transition to... + PCODE osrMethodCode = ppInfo->m_osrMethodCode; _ASSERTE(osrMethodCode != (PCODE)NULL); + return osrMethodCode; +} + +// Jit helper invoked at a patchpoint. +// +// Checks to see if this is a known patchpoint, if not, +// an entry is added to the patchpoint table. +// +// When the patchpoint has been hit often enough to trigger +// a transition, create an OSR method. OR if the first argument +// is NULL, always create an OSR method and transition to it. +// +// Currently, counter(the first argument) is a pointer into the Tier0 method stack +// frame if it exists so we have exclusive access. + +extern "C" void JIT_PatchpointWorkerWorkerWithPolicy(TransitionBlock * pTransitionBlock) +{ + // BEGIN_PRESERVE_LAST_ERROR; + DWORD dwLastError = ::GetLastError(); + + // This method may not return normally + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_COOPERATIVE; + + PTR_PCODE pReturnAddress = (PTR_PCODE)(((BYTE*)pTransitionBlock) + TransitionBlock::GetOffsetOfReturnAddress()); + PCODE ip = *pReturnAddress; + int* counter = *(int**)GetFirstArgumentRegisterValuePtr(pTransitionBlock); + int ilOffset = *(int*)GetSecondArgumentRegisterValuePtr(pTransitionBlock); + int hitCount = 1; // This will stay at 1 for forced transition scenarios, but will be updated to the actual hit count for normal patch points + + // Patchpoint identity is the helper return address + + // Fetch or setup patchpoint info for this patchpoint. + EECodeInfo codeInfo(ip); + MethodDesc* pMD = codeInfo.GetMethodDesc(); + LoaderAllocator* allocator = pMD->GetLoaderAllocator(); + OnStackReplacementManager* manager = allocator->GetOnStackReplacementManager(); + PerPatchpointInfo * ppInfo = manager->GetPerPatchpointInfo(ip); + +#ifdef _DEBUG + const int ppId = ppInfo->m_patchpointId; +#endif + + bool isNewMethod = false; + PCODE osrMethodCode = (PCODE)NULL; + + + bool patchpointMustFindOptimizedCode = counter == NULL; + + if (patchpointMustFindOptimizedCode) + { + osrMethodCode = PatchpointRequiredPolicy(pTransitionBlock, counter, ilOffset, ppInfo, codeInfo, &isNewMethod); + } + else + { + osrMethodCode = PatchpointOptimizationPolicy(pTransitionBlock, counter, ilOffset, ppInfo, codeInfo, &isNewMethod); + } + + if (osrMethodCode == (PCODE)NULL) + { + _ASSERTE(!patchpointMustFindOptimizedCode); + goto DONE; + } + + // If we get here, we have code to transition to... + { Thread *pThread = GetThread(); @@ -2676,243 +2840,10 @@ void JIT_Patchpoint(int* counter, int ilOffset) ::SetLastError(dwLastError); } -// Jit helper invoked at a partial compilation patchpoint. -// -// Similar to Jit_Patchpoint, but invoked when execution -// reaches a point in a method where the continuation -// was never jitted (eg an exceptional path). -// -// Unlike regular patchpoints, partial compilation patchpoints -// must always transition. -// -HCIMPL1(VOID, JIT_PartialCompilationPatchpoint, int ilOffset) -{ - FCALL_CONTRACT; - - // BEGIN_PRESERVE_LAST_ERROR; - DWORD dwLastError = ::GetLastError(); - PerPatchpointInfo* ppInfo = NULL; - bool isNewMethod = false; - CONTEXT* pFrameContext = NULL; - -#if !defined(TARGET_WINDOWS) || !defined(TARGET_AMD64) - CONTEXT originalFrameContext; - originalFrameContext.ContextFlags = CONTEXT_FULL; - pFrameContext = &originalFrameContext; -#endif - - // Patchpoint identity is the helper return address - PCODE ip = (PCODE)_ReturnAddress(); - -#ifdef _DEBUG - // Friendly ID number - int ppId = 0; -#endif - - HELPER_METHOD_FRAME_BEGIN_0(); - - // Fetch or setup patchpoint info for this patchpoint. - EECodeInfo codeInfo(ip); - MethodDesc* pMD = codeInfo.GetMethodDesc(); - LoaderAllocator* allocator = pMD->GetLoaderAllocator(); - OnStackReplacementManager* manager = allocator->GetOnStackReplacementManager(); - ppInfo = manager->GetPerPatchpointInfo(ip); - -#ifdef _DEBUG - ppId = ppInfo->m_patchpointId; -#endif - - // See if we have an OSR method for this patchpoint. - DWORD backoffs = 0; - - // Enable GC while we jit or wait for the continuation to be jitted. - { - GCX_PREEMP(); - - while (ppInfo->m_osrMethodCode == (PCODE)NULL) - { - // Invalid patchpoints are fatal, for partial compilation patchpoints - // - if ((ppInfo->m_flags & PerPatchpointInfo::patchpoint_invalid) == PerPatchpointInfo::patchpoint_invalid) - { - LOG((LF_TIEREDCOMPILATION, LL_FATALERROR, "Jit_PartialCompilationPatchpoint: invalid patchpoint [%d] (0x%p) in Method=0x%pM (%s::%s) at offset %d\n", - ppId, ip, pMD, pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, ilOffset)); - EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); - } - - // Make sure no other thread is trying to create the OSR method. - // - LONG oldFlags = ppInfo->m_flags; - if ((oldFlags & PerPatchpointInfo::patchpoint_triggered) == PerPatchpointInfo::patchpoint_triggered) - { - LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "Jit_PartialCompilationPatchpoint: AWAITING OSR method for patchpoint [%d] (0x%p)\n", ppId, ip)); - __SwitchToThread(0, backoffs++); - continue; - } - - // Make sure we win the race to create the OSR method - // - LONG newFlags = ppInfo->m_flags | PerPatchpointInfo::patchpoint_triggered; - BOOL triggerTransition = InterlockedCompareExchange(&ppInfo->m_flags, newFlags, oldFlags) == oldFlags; - - if (!triggerTransition) - { - LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "Jit_PartialCompilationPatchpoint: (lost race) AWAITING OSR method for patchpoint [%d] (0x%p)\n", ppId, ip)); - __SwitchToThread(0, backoffs++); - continue; - } - - // Invoke the helper to build the OSR method - // - // TODO: may not want to optimize this part of the method, if it's truly partial compilation - // and can't possibly rejoin into the main flow. - // - // (but consider: throw path in method with try/catch, OSR method will contain more than just the throw?) - // - LOG((LF_TIEREDCOMPILATION, LL_INFO10, "Jit_PartialCompilationPatchpoint: patchpoint [%d] (0x%p) TRIGGER\n", ppId, ip)); - PCODE newMethodCode = JitPatchpointWorker(pMD, codeInfo, ilOffset); - - // If that failed, mark the patchpoint as invalid. - // This is fatal, for partial compilation patchpoints - // - if (newMethodCode == (PCODE)NULL) - { - STRESS_LOG3(LF_TIEREDCOMPILATION, LL_WARNING, "Jit_PartialCompilationPatchpoint: patchpoint (0x%p) OSR method creation failed," - " marking patchpoint invalid for Method=0x%pM il offset %d\n", ip, pMD, ilOffset); - InterlockedOr(&ppInfo->m_flags, (LONG)PerPatchpointInfo::patchpoint_invalid); - EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); - break; - } - - // We've successfully created the osr method; make it available. - _ASSERTE(ppInfo->m_osrMethodCode == (PCODE)NULL); - ppInfo->m_osrMethodCode = newMethodCode; - isNewMethod = true; - } - } - - // If we get here, we have code to transition to... - PCODE osrMethodCode = ppInfo->m_osrMethodCode; - _ASSERTE(osrMethodCode != (PCODE)NULL); - - Thread *pThread = GetThread(); - -#ifdef FEATURE_HIJACK - // We can't crawl the stack of a thread that currently has a hijack pending - // (since the hijack routine won't be recognized by any code manager). So we - // Undo any hijack, the EE will re-attempt it later. - pThread->UnhijackThread(); -#endif - - // Find context for the original method -#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) - DWORD contextSize = 0; - ULONG64 xStateCompactionMask = 0; - DWORD contextFlags = CONTEXT_FULL; - if (Thread::AreShadowStacksEnabled()) - { - xStateCompactionMask = XSTATE_MASK_CET_U; - contextFlags |= CONTEXT_XSTATE; - } - - // The initialize call should fail but return contextSize - BOOL success = g_pfnInitializeContext2 ? - g_pfnInitializeContext2(NULL, contextFlags, NULL, &contextSize, xStateCompactionMask) : - InitializeContext(NULL, contextFlags, NULL, &contextSize); - - _ASSERTE(!success && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)); - - PVOID pBuffer = _alloca(contextSize); - success = g_pfnInitializeContext2 ? - g_pfnInitializeContext2(pBuffer, contextFlags, &pFrameContext, &contextSize, xStateCompactionMask) : - InitializeContext(pBuffer, contextFlags, &pFrameContext, &contextSize); - _ASSERTE(success); -#else // TARGET_WINDOWS && TARGET_AMD64 - _ASSERTE(pFrameContext != nullptr); -#endif // TARGET_WINDOWS && TARGET_AMD64 - - // Find context for the original method - RtlCaptureContext(pFrameContext); - -#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) - if (Thread::AreShadowStacksEnabled()) - { - pFrameContext->ContextFlags |= CONTEXT_XSTATE; - SetXStateFeaturesMask(pFrameContext, xStateCompactionMask); - SetSSP(pFrameContext, _rdsspq()); - } -#endif // TARGET_WINDOWS && TARGET_AMD64 - - // Walk back to the original method frame - Thread::VirtualUnwindToFirstManagedCallFrame(pFrameContext); - - // Remember original method FP and SP because new method will inherit them. - UINT_PTR currentSP = GetSP(pFrameContext); - UINT_PTR currentFP = GetFP(pFrameContext); - - // We expect to be back at the right IP - if ((UINT_PTR)ip != GetIP(pFrameContext)) - { - // Should be fatal - STRESS_LOG2(LF_TIEREDCOMPILATION, LL_INFO10, "Jit_PartialCompilationPatchpoint: patchpoint (0x%p) TRANSITION" - " unexpected context IP 0x%p\n", ip, GetIP(pFrameContext)); - EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); - } - - // Now unwind back to the original method caller frame. - EECodeInfo callerCodeInfo(GetIP(pFrameContext)); - ULONG_PTR establisherFrame = 0; - PVOID handlerData = NULL; - RtlVirtualUnwind(UNW_FLAG_NHANDLER, callerCodeInfo.GetModuleBase(), GetIP(pFrameContext), callerCodeInfo.GetFunctionEntry(), - pFrameContext, &handlerData, &establisherFrame, NULL); - - // Now, set FP and SP back to the values they had just before this helper was called, - // since the new method must have access to the original method frame. - // - // TODO: if we access the patchpointInfo here, we can read out the FP-SP delta from there and - // use that to adjust the stack, likely saving some stack space. - -#if defined(TARGET_AMD64) - // If calls push the return address, we need to simulate that here, so the OSR - // method sees the "expected" SP misalgnment on entry. - _ASSERTE(currentSP % 16 == 0); - currentSP -= 8; - -#if defined(TARGET_WINDOWS) - DWORD64 ssp = GetSSP(pFrameContext); - if (ssp != 0) - { - SetSSP(pFrameContext, ssp - 8); - } -#endif // TARGET_WINDOWS - - pFrameContext->Rbp = currentFP; -#endif // TARGET_AMD64 - - SetSP(pFrameContext, currentSP); - - // Note we can get here w/o triggering, if there is an existing OSR method and - // we hit the patchpoint. - const int transitionLogLevel = isNewMethod ? LL_INFO10 : LL_INFO1000; - LOG((LF_TIEREDCOMPILATION, transitionLogLevel, "Jit_PartialCompilationPatchpoint: patchpoint [%d] (0x%p) TRANSITION to ip 0x%p\n", ppId, ip, osrMethodCode)); - - // Install new entry point as IP - SetIP(pFrameContext, osrMethodCode); - - // This method doesn't return normally so we have to manually restore things. - HELPER_METHOD_FRAME_END(); - ENDFORBIDGC(); - ::SetLastError(dwLastError); - - // Transition! - __asan_handle_no_return(); - ClrRestoreNonvolatileContext(pFrameContext); -} -HCIMPLEND #else -void JIT_Patchpoint(int* counter, int ilOffset) +HCIMPL2(void, JIT_Patchpoint, int* counter, int ilOffset) { // Stub version if OSR feature is disabled // @@ -2920,6 +2851,7 @@ void JIT_Patchpoint(int* counter, int ilOffset) UNREACHABLE(); } +HCIMPLEND HCIMPL1(VOID, JIT_PartialCompilationPatchpoint, int ilOffset) { diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index e284b74639202c..35e06c4041e482 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -14537,7 +14537,7 @@ TADDR EECodeInfo::GetSavedMethodCode() return GetStartAddress(); } -TADDR EECodeInfo::GetStartAddress() +TADDR EECodeInfo::GetStartAddress() const { CONTRACTL { NOTHROW; @@ -14548,7 +14548,7 @@ TADDR EECodeInfo::GetStartAddress() return m_pJM->JitTokenToStartAddress(m_methodToken); } -NativeCodeVersion EECodeInfo::GetNativeCodeVersion() +NativeCodeVersion EECodeInfo::GetNativeCodeVersion() const { CONTRACTL { diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 1d20db72f5f244..7f1835e458a53a 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -103,6 +103,10 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, #endif // TARGET_X86 +// These must be implemented in assembly and generate a TransitionBlock then calling JIT_PatchpointWorkerWithPolicy in order to actually be used. +EXTERN_C FCDECL2(void, JIT_Patchpoint, int* counter, int ilOffset); +EXTERN_C FCDECL1(void, JIT_PartialCompilationPatchpoint, int ilOffset); + // // JIT HELPER ALIASING FOR PORTABILITY. // diff --git a/src/coreclr/vm/loongarch64/asmhelpers.S b/src/coreclr/vm/loongarch64/asmhelpers.S index 6be20320f9850c..abf769e7b52e64 100644 --- a/src/coreclr/vm/loongarch64/asmhelpers.S +++ b/src/coreclr/vm/loongarch64/asmhelpers.S @@ -1091,6 +1091,22 @@ NESTED_ENTRY OnCallCountThresholdReachedStub, _TEXT, NoHandler EPILOG_BRANCH_REG $t4 NESTED_END OnCallCountThresholdReachedStub, _TEXT +NESTED_ENTRY JIT_Patchpoint, _TEXT, NoHandler + PROLOG_WITH_TRANSITION_BLOCK + + addi.d $a0, $sp, __PWTB_TransitionBlock // TransitionBlock * + bl C_FUNC(JIT_PatchpointWorkerWorkerWithPolicy) + + EPILOG_WITH_TRANSITION_BLOCK_RETURN +NESTED_END JIT_Patchpoint, _TEXT + +// first arg register holds iloffset, which needs to be moved to the second register, and the first register filled with NULL +LEAF_ENTRY JIT_PartialCompilationPatchpoint, _TEXT + move $a1, $a0 + li.d $a0, 0 + b C_FUNC(JIT_Patchpoint) +LEAF_END JIT_PartialCompilationPatchpoint, _TEXT + #endif // FEATURE_TIERED_COMPILATION // ------------------------------------------------------------------ diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index a816f93b50c13b..ce0e1049654fe9 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -3558,17 +3558,6 @@ static PCODE getHelperForStaticBase(Module * pModule, CORCOMPILE_FIXUP_BLOB_KIND return pHelper; } -TADDR GetFirstArgumentRegisterValuePtr(TransitionBlock * pTransitionBlock) -{ - TADDR pArgument = (TADDR)pTransitionBlock + TransitionBlock::GetOffsetOfArgumentRegisters(); -#ifdef TARGET_X86 - // x86 is special as always - pArgument += offsetof(ArgumentRegisters, ECX); -#endif - - return pArgument; -} - void ProcessDynamicDictionaryLookup(TransitionBlock * pTransitionBlock, Module * pModule, ModuleBase * pInfoModule, diff --git a/src/coreclr/vm/riscv64/asmhelpers.S b/src/coreclr/vm/riscv64/asmhelpers.S index 12b2918ae31e63..75c5f7609c93bf 100644 --- a/src/coreclr/vm/riscv64/asmhelpers.S +++ b/src/coreclr/vm/riscv64/asmhelpers.S @@ -958,6 +958,22 @@ NESTED_ENTRY OnCallCountThresholdReachedStub, _TEXT, NoHandler EPILOG_BRANCH_REG t4 NESTED_END OnCallCountThresholdReachedStub, _TEXT +NESTED_ENTRY JIT_Patchpoint, _TEXT, NoHandler + PROLOG_WITH_TRANSITION_BLOCK + + addi a0, sp, __PWTB_TransitionBlock // TransitionBlock * + call C_FUNC(JIT_PatchpointWorkerWorkerWithPolicy) + + EPILOG_WITH_TRANSITION_BLOCK_RETURN +NESTED_END JIT_Patchpoint, _TEXT + +// first arg register holds iloffset, which needs to be moved to the second register, and the first register filled with NULL +LEAF_ENTRY JIT_PartialCompilationPatchpoint, _TEXT + mv a1, a0 + li a0, 0 + j C_FUNC(JIT_Patchpoint) +LEAF_END JIT_PartialCompilationPatchpoint, _TEXT + #endif // FEATURE_TIERED_COMPILATION // ------------------------------------------------------------------ diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.NativeAOT.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.NativeAOT.sfxproj index 336574ffe10fc7..34587e9fa80607 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.NativeAOT.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.NativeAOT.sfxproj @@ -20,8 +20,11 @@ + + -To enable the tests marked with [ConditionalFact(nameof(IsLdapConfigurationExist))], you need to setup an LDAP server and provide the needed server info here. +To enable the tests marked with [ConditionalFact(nameof(IsLdapConfigurationExist))], you need to setup an LDAP server as described below and set the environment variable LDAP_TEST_SERVER_INDEX to the appropriate offset into the XML section found at the end of this file. To ship, we should test on both an Active Directory LDAP server, and at least one other server, as behaviors are a little different. However for local testing, it is easiest to connect to an OpenDJ LDAP server in a docker container (eg., in WSL2). -When testing with later of versions of LDAP, the ldapsearch commands below may need to use - - -H ldap://localhost: - -instead of - - -h localhost -p - OPENDJ SERVER ============= @@ -19,7 +11,7 @@ OPENDJ SERVER test it with this command - it should return some results in WSL2 - ldapsearch -h localhost -p 1389 -D 'cn=admin,dc=example,dc=com' -x -w password + ldapsearch -H ldap://localhost:1389 -D 'cn=admin,dc=example,dc=com' -x -w password this command views the status @@ -32,16 +24,16 @@ SLAPD OPENLDAP SERVER and to test and view status - ldapsearch -h localhost -p 390 -D 'cn=admin,dc=example,dc=com' -x -w password + ldapsearch -H ldap://localhost:390 -D 'cn=admin,dc=example,dc=com' -x -w password docker exec -it slapd01 slapcat SLAPD OPENLDAP SERVER WITH TLS ============================== -The osixia/openldap container image automatically creates a TLS lisener with a self-signed certificate. This can be used to test TLS. +The osixia/openldap container image automatically creates a TLS listener with a self-signed certificate. This can be used to test TLS. -Start the container, with TLS on port 1636, without client certificate verification: +Start the container, with TLS on port 1636, but without client certificate verification: docker run --publish 1389:389 --publish 1636:636 --name ldap --hostname ldap.local --detach --rm --env LDAP_TLS_VERIFY_CLIENT=never --env LDAP_ADMIN_PASSWORD=password osixia/openldap --loglevel debug @@ -64,6 +56,8 @@ To test and view the status: ldapsearch -H ldaps://ldap.local:1636 -b dc=example,dc=org -x -D cn=admin,dc=example,dc=org -w password +use '-d 1' or '-d 2' for debugging. + ACTIVE DIRECTORY ================ @@ -73,7 +67,7 @@ When running against Active Directory from a Windows client, you should not see If you are running your AD server as a VM on the same machine that you are running WSL2, you must execute this command on the host to bridge the two Hyper-V networks so that it is visible from WSL2: - Get-NetIPInterface | where {$_.InterfaceAlias -eq 'vEthernet (WSL)' -or $_.InterfaceAlias -eq 'vEthernet (Default Switch)'} | Set-NetIPInterface -Forwarding Enabled + Get-NetIPInterface | where {$_.InterfaceAlias -eq 'vEthernet (WSL)' -or $_.InterfaceAlias -eq 'vEthernet (Default Switch)'} | Set-NetIPInterface -Forwarding Enabled The WSL2 VM should now be able to see the AD VM by IP address. To make it visible by host name, it's probably easiest to just add it to /etc/hosts. @@ -90,7 +84,7 @@ Note: @@ -113,15 +107,6 @@ Note: ServerBind,None False - - danmose-ldap.danmose-domain.com - DC=danmose-domain,DC=com - 389 - danmose-domain\Administrator - %TESTPASSWORD% - ServerBind,None - True - ldap.local DC=example,DC=org @@ -132,5 +117,14 @@ Note: true False + + danmose-ldap.danmose-domain.com + DC=danmose-domain,DC=com + 389 + danmose-domain\Administrator + %TESTPASSWORD% + ServerBind,None + True + diff --git a/src/libraries/System.Collections.Immutable/ref/System.Collections.Immutable.cs b/src/libraries/System.Collections.Immutable/ref/System.Collections.Immutable.cs index 24f0119fcc29cd..5df454fe3b3bf7 100644 --- a/src/libraries/System.Collections.Immutable/ref/System.Collections.Immutable.cs +++ b/src/libraries/System.Collections.Immutable/ref/System.Collections.Immutable.cs @@ -1302,5 +1302,6 @@ public static partial class ImmutableCollectionsMarshal { public static System.Collections.Immutable.ImmutableArray AsImmutableArray(T[]? array) { throw null; } public static T[]? AsArray(System.Collections.Immutable.ImmutableArray array) { throw null; } + public static System.Memory AsMemory(System.Collections.Immutable.ImmutableArray.Builder? builder) { throw null; } } } diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs index e6e36c81339bd8..9f596e8f7578a7 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs @@ -1095,6 +1095,9 @@ private void RemoveAtRange(ICollection indicesToRemove) _count -= indicesToRemove.Count; } + + /// Gets a for the filled portion of the backing array. + internal Memory AsMemory() => new(_elements, 0, _count); } } } diff --git a/src/libraries/System.Collections.Immutable/src/System/Runtime.InteropServices/ImmutableCollectionsMarshal.cs b/src/libraries/System.Collections.Immutable/src/System/Runtime.InteropServices/ImmutableCollectionsMarshal.cs index 737a3a862a2d47..cb751a4283df5e 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Runtime.InteropServices/ImmutableCollectionsMarshal.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Runtime.InteropServices/ImmutableCollectionsMarshal.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; using System.Collections.Immutable; namespace System.Runtime.InteropServices @@ -54,5 +55,19 @@ public static ImmutableArray AsImmutableArray(T[]? array) { return array.array; } + + /// + /// Gets a for the array underlying an input . + /// + /// The type of elements in the input value. + /// The builder. + /// + /// A for the filled portion of array underlying + /// the input . + /// + public static Memory AsMemory(ImmutableArray.Builder? builder) + { + return builder?.AsMemory() ?? default; + } } } diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableCollectionsMarshal.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableCollectionsMarshal.cs index 76ed476ee571ef..6fc463bfd9268c 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableCollectionsMarshal.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableCollectionsMarshal.cs @@ -135,6 +135,59 @@ static void Test() Test(); } + [Fact] + public void AsMemoryFromNullBuilder() + { + Memory m = ImmutableCollectionsMarshal.AsMemory(null); + Assert.True(m.IsEmpty); + Assert.True(MemoryMarshal.TryGetArray(m, out ArraySegment segment)); + Assert.NotNull(segment.Array); + Assert.Equal(0, segment.Array.Length); + Assert.Equal(0, segment.Offset); + Assert.Equal(0, segment.Count); + } + + [Fact] + public void AsMemoryFromEmptyBuilder() + { + Memory m = ImmutableCollectionsMarshal.AsMemory(ImmutableArray.CreateBuilder()); + Assert.True(m.IsEmpty); + Assert.True(MemoryMarshal.TryGetArray(m, out ArraySegment segment)); + Assert.NotNull(segment.Array); + Assert.Equal(0, segment.Offset); + Assert.Equal(0, segment.Count); + } + + [Fact] + public void AsMemoryFromNonEmptyBuilder() + { + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(1); + builder.Add(42); + builder.Add(43); + builder.Add(44); + + Memory m1 = ImmutableCollectionsMarshal.AsMemory(builder); + Assert.Equal(3, m1.Length); + + Assert.True(MemoryMarshal.TryGetArray(m1, out ArraySegment segment1)); + Assert.NotNull(segment1.Array); + Assert.Equal(0, segment1.Offset); + Assert.Equal(3, segment1.Count); + + Span span = m1.Span; + Assert.Equal(42, span[0]); + Assert.Equal(43, span[1]); + Assert.Equal(44, span[2]); + + Memory m2 = ImmutableCollectionsMarshal.AsMemory(builder); + Assert.Equal(3, m2.Length); + Assert.True(MemoryMarshal.TryGetArray(m2, out ArraySegment segment2)); + + Assert.Same(segment1.Array, segment2.Array); + Assert.Equal(segment1.Offset, segment2.Offset); + Assert.Equal(segment1.Count, segment2.Count); + } + public class CustomClass { public object Foo; diff --git a/src/libraries/System.DirectoryServices.Protocols/ref/System.DirectoryServices.Protocols.cs b/src/libraries/System.DirectoryServices.Protocols/ref/System.DirectoryServices.Protocols.cs index cf1b9154d7befe..3559adc9703731 100644 --- a/src/libraries/System.DirectoryServices.Protocols/ref/System.DirectoryServices.Protocols.cs +++ b/src/libraries/System.DirectoryServices.Protocols/ref/System.DirectoryServices.Protocols.cs @@ -382,6 +382,8 @@ public partial class LdapSessionOptions internal LdapSessionOptions() { } public bool AutoReconnect { get { throw null; } set { } } public string DomainName { get { throw null; } set { } } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")] + public string TrustedCertificatesDirectory { get { throw null; } set { } } public string HostName { get { throw null; } set { } } public bool HostReachable { get { throw null; } } public System.DirectoryServices.Protocols.LocatorFlags LocatorFlag { get { throw null; } set { } } @@ -402,6 +404,8 @@ internal LdapSessionOptions() { } public bool Signing { get { throw null; } set { } } public System.DirectoryServices.Protocols.SecurityPackageContextConnectionInformation SslInformation { get { throw null; } } public int SspiFlag { get { throw null; } set { } } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")] + public void StartNewTlsSessionContext() { } public bool TcpKeepAlive { get { throw null; } set { } } public System.DirectoryServices.Protocols.VerifyServerCertificateCallback VerifyServerCertificate { get { throw null; } set { } } public void FastConcurrentBind() { } diff --git a/src/libraries/System.DirectoryServices.Protocols/src/Resources/Strings.resx b/src/libraries/System.DirectoryServices.Protocols/src/Resources/Strings.resx index b63f103619fbdb..1f6c9734a7384c 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/Resources/Strings.resx +++ b/src/libraries/System.DirectoryServices.Protocols/src/Resources/Strings.resx @@ -426,4 +426,7 @@ Only ReferralChasingOptions.None and ReferralChasingOptions.All are supported on Linux. + + The directory '{0}' does not exist. + \ No newline at end of file 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 1125bfd568d385..facdfc6a484bb9 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 @@ -955,13 +955,13 @@ private unsafe Interop.BOOL ProcessClientCertificate(IntPtr ldapHandle, IntPtr C private void Connect() { - //Ccurrently ldap does not accept more than one certificate. + // Currently ldap does not accept more than one certificate. if (ClientCertificates.Count > 1) { throw new InvalidOperationException(SR.InvalidClientCertificates); } - // Set the certificate callback routine here if user adds the certifcate to the certificate collection. + // Set the certificate callback routine here if user adds the certificate to the certificate collection. if (ClientCertificates.Count != 0) { int certError = LdapPal.SetClientCertOption(_ldapHandle, LdapOption.LDAP_OPT_CLIENT_CERTIFICATE, _clientCertificateRoutine); diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Linux.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Linux.cs index e1cfffebb531fc..5059c40499d5c6 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Linux.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Linux.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; +using System.IO; +using System.Runtime.Versioning; namespace System.DirectoryServices.Protocols { @@ -11,6 +13,34 @@ public partial class LdapSessionOptions private bool _secureSocketLayer; + /// + /// Specifies the path of the directory containing CA certificates in the PEM format. + /// Multiple directories may be specified by separating with a semi-colon. + /// + /// + /// The certificate files are looked up by the CA subject name hash value where that hash can be + /// obtained by using, for example, openssl x509 -hash -noout -in CA.crt. + /// It is a common practice to have the certificate file be a symbolic link to the actual certificate file + /// which can be done by using openssl rehash . or c_rehash . in the directory + /// containing the certificate files. + /// + /// The directory not exist. + [UnsupportedOSPlatform("windows")] + public string TrustedCertificatesDirectory + { + get => GetStringValueHelper(LdapOption.LDAP_OPT_X_TLS_CACERTDIR, releasePtr: true); + + set + { + if (!Directory.Exists(value)) + { + throw new DirectoryNotFoundException(SR.Format(SR.DirectoryNotFound, value)); + } + + SetStringOptionHelper(LdapOption.LDAP_OPT_X_TLS_CACERTDIR, value); + } + } + public bool SecureSocketLayer { get @@ -52,6 +82,16 @@ public ReferralChasingOptions ReferralChasing } } + /// + /// Create a new TLS library context. + /// Calling this is necessary after setting TLS-based options, such as TrustedCertificatesDirectory. + /// + [UnsupportedOSPlatform("windows")] + public void StartNewTlsSessionContext() + { + SetIntValueHelper(LdapOption.LDAP_OPT_X_TLS_NEWCTX, 0); + } + private bool GetBoolValueHelper(LdapOption option) { if (_connection._disposed) throw new ObjectDisposedException(GetType().Name); @@ -71,5 +111,14 @@ private void SetBoolValueHelper(LdapOption option, bool value) ErrorChecking.CheckAndSetLdapError(error); } + + private void SetStringOptionHelper(LdapOption option, string value) + { + if (_connection._disposed) throw new ObjectDisposedException(GetType().Name); + + int error = LdapPal.SetStringOption(_connection._ldapHandle, option, value); + + ErrorChecking.CheckAndSetLdapError(error); + } } } diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Windows.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Windows.cs index 813005c5ecb72b..cc73449104adf4 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Windows.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Windows.cs @@ -10,6 +10,13 @@ public partial class LdapSessionOptions { private static void PALCertFreeCRLContext(IntPtr certPtr) => Interop.Ldap.CertFreeCRLContext(certPtr); + [UnsupportedOSPlatform("windows")] + public string TrustedCertificatesDirectory + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + public bool SecureSocketLayer { get @@ -24,6 +31,9 @@ public bool SecureSocketLayer } } + [UnsupportedOSPlatform("windows")] + public void StartNewTlsSessionContext() => throw new PlatformNotSupportedException(); + public int ProtocolVersion { get => GetIntValueHelper(LdapOption.LDAP_OPT_VERSION); diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs index 64c8487c7b481e..4403cb8a702483 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs @@ -2,12 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.Diagnostics; using System.DirectoryServices.Tests; using System.Globalization; +using System.IO; using System.Net; -using System.Text; -using System.Threading; using Xunit; namespace System.DirectoryServices.Protocols.Tests @@ -16,6 +14,7 @@ public partial class DirectoryServicesProtocolsTests { internal static bool LdapConfigurationExists => LdapConfiguration.Configuration != null; internal static bool IsActiveDirectoryServer => LdapConfigurationExists && LdapConfiguration.Configuration.IsActiveDirectoryServer; + internal static bool UseTls => LdapConfigurationExists && LdapConfiguration.Configuration.UseTls; internal static bool IsServerSideSortSupported => LdapConfigurationExists && LdapConfiguration.Configuration.SupportsServerSideSort; @@ -706,6 +705,64 @@ public void TestMultipleServerBind() connection.Timeout = new TimeSpan(0, 3, 0); } +#if NET + [ConditionalFact(nameof(UseTls))] + [PlatformSpecific(TestPlatforms.Linux)] + public void StartNewTlsSessionContext() + { + using (var connection = GetConnection(bind: false)) + { + // We use "." as the directory since it must be a valid directory for StartNewTlsSessionContext() + Bind() to be successful even + // though there are no client certificates in ".". + connection.SessionOptions.TrustedCertificatesDirectory = "."; + + // For a real-world scenario, we would call 'StartTransportLayerSecurity(null)' here which would do the TLS handshake including + // providing the client certificate to the server and validating the server certificate. However, this requires additional + // setup that we don't have including trusting the server certificate and by specifying "demand" in the setup of the server + // via 'LDAP_TLS_VERIFY_CLIENT=demand' to force the TLS handshake to occur. + + connection.SessionOptions.StartNewTlsSessionContext(); + connection.Bind(); + + SearchRequest searchRequest = new (LdapConfiguration.Configuration.SearchDn, "(objectClass=*)", SearchScope.Subtree); + _ = (SearchResponse)connection.SendRequest(searchRequest); + } + } + + [ConditionalFact(nameof(UseTls))] + [PlatformSpecific(TestPlatforms.Linux)] + public void StartNewTlsSessionContext_ThrowsLdapException() + { + using (var connection = GetConnection(bind: false)) + { + // Create a new session context without setting TrustedCertificatesDirectory. + connection.SessionOptions.StartNewTlsSessionContext(); + Assert.Throws(() => connection.Bind()); + } + } + + [ConditionalFact(nameof(LdapConfigurationExists))] + [PlatformSpecific(TestPlatforms.Linux)] + public void TrustedCertificatesDirectory_ThrowsDirectoryNotFoundException() + { + using (var connection = GetConnection(bind: false)) + { + Assert.Throws(() => connection.SessionOptions.TrustedCertificatesDirectory = "nonexistent"); + } + } + + [ConditionalFact(nameof(LdapConfigurationExists))] + [PlatformSpecific(TestPlatforms.Windows)] + public void StartNewTlsSessionContext_ThrowsPlatformNotSupportedException() + { + using (var connection = new LdapConnection("server")) + { + LdapSessionOptions options = connection.SessionOptions; + Assert.Throws(() => options.StartNewTlsSessionContext()); + } + } +#endif + private void DeleteAttribute(LdapConnection connection, string entryDn, string attributeName) { string dn = entryDn + "," + LdapConfiguration.Configuration.SearchDn; @@ -786,24 +843,24 @@ private SearchResultEntry SearchUser(LdapConnection connection, string rootDn, s return null; } - private LdapConnection GetConnection(string server) + private static LdapConnection GetConnection(string server) { LdapDirectoryIdentifier directoryIdentifier = new LdapDirectoryIdentifier(server, fullyQualifiedDnsHostName: true, connectionless: false); return GetConnection(directoryIdentifier); } - private LdapConnection GetConnection() + private static LdapConnection GetConnection(bool bind = true) { LdapDirectoryIdentifier directoryIdentifier = string.IsNullOrEmpty(LdapConfiguration.Configuration.Port) ? new LdapDirectoryIdentifier(LdapConfiguration.Configuration.ServerName, fullyQualifiedDnsHostName: true, connectionless: false) : new LdapDirectoryIdentifier(LdapConfiguration.Configuration.ServerName, int.Parse(LdapConfiguration.Configuration.Port, NumberStyles.None, CultureInfo.InvariantCulture), fullyQualifiedDnsHostName: true, connectionless: false); - return GetConnection(directoryIdentifier); + return GetConnection(directoryIdentifier, bind); } - private static LdapConnection GetConnection(LdapDirectoryIdentifier directoryIdentifier) + private static LdapConnection GetConnection(LdapDirectoryIdentifier directoryIdentifier, bool bind = true) { NetworkCredential credential = new NetworkCredential(LdapConfiguration.Configuration.UserName, LdapConfiguration.Configuration.Password); @@ -816,7 +873,11 @@ private static LdapConnection GetConnection(LdapDirectoryIdentifier directoryIde // to LDAP v2, which we do not support, and will return LDAP_PROTOCOL_ERROR connection.SessionOptions.ProtocolVersion = 3; connection.SessionOptions.SecureSocketLayer = LdapConfiguration.Configuration.UseTls; - connection.Bind(); + + if (bind) + { + connection.Bind(); + } connection.Timeout = new TimeSpan(0, 3, 0); return connection; diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesTestHelpers.cs b/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesTestHelpers.cs index 4a11570d810344..66838588a282a7 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesTestHelpers.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesTestHelpers.cs @@ -29,14 +29,18 @@ public static bool IsLibLdapInstalled } else { - _isLibLdapInstalled = NativeLibrary.TryLoad("libldap-2.4.so.2", out _); + _isLibLdapInstalled = + NativeLibrary.TryLoad("libldap.so.2", out _) || + NativeLibrary.TryLoad("libldap-2.6.so.0", out _) || + NativeLibrary.TryLoad("libldap-2.5.so.0", out _) || + NativeLibrary.TryLoad("libldap-2.4.so.2", out _); } } - return _isLibLdapInstalled.Value; #else _isLibLdapInstalled = true; // In .NET Framework ldap is always installed. - return _isLibLdapInstalled.Value; #endif + + return _isLibLdapInstalled.Value; } } } diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/LdapSessionOptionsTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/LdapSessionOptionsTests.cs index 5f6a737834ac23..2a8ab23a16d421 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/LdapSessionOptionsTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/LdapSessionOptionsTests.cs @@ -7,6 +7,8 @@ namespace System.DirectoryServices.Protocols.Tests { + // To enable these tests locally for Mono, comment out this line in DirectoryServicesTestHelpers.cs: + // [assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/35912", TestRuntimes.Mono)] [ConditionalClass(typeof(DirectoryServicesTestHelpers), nameof(DirectoryServicesTestHelpers.IsWindowsOrLibLdapIsInstalled))] public class LdapSessionOptionsTests { @@ -27,6 +29,7 @@ public void ReferralChasing_Set_GetReturnsExpected_On_Windows(ReferralChasingOpt } [Theory] + [ActiveIssue("https://github.com/dotnet/runtime/issues/112146")] [PlatformSpecific(TestPlatforms.Linux)] [InlineData(ReferralChasingOptions.None)] [InlineData(ReferralChasingOptions.All)] @@ -756,5 +759,32 @@ public void StopTransportLayerSecurity_Disposed_ThrowsObjectDisposedException() Assert.Throws(() => connection.SessionOptions.StopTransportLayerSecurity()); } + +#if NET + [Fact] + [PlatformSpecific(TestPlatforms.Linux)] + public void CertificateDirectoryProperty() + { + using (var connection = new LdapConnection("server")) + { + LdapSessionOptions options = connection.SessionOptions; + Assert.Null(options.TrustedCertificatesDirectory); + + options.TrustedCertificatesDirectory = "."; + Assert.Equal(".", options.TrustedCertificatesDirectory); + } + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public void CertificateDirectoryProperty_ThrowsPlatformNotSupportedException() + { + using (var connection = new LdapConnection("server")) + { + LdapSessionOptions options = connection.SessionOptions; + Assert.Throws(() => options.TrustedCertificatesDirectory = "CertificateDirectory"); + } + } +#endif } } diff --git a/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/ArrayRecord.cs b/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/ArrayRecord.cs index c18208668225f8..16fccd99cc1c5f 100644 --- a/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/ArrayRecord.cs +++ b/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/ArrayRecord.cs @@ -64,6 +64,11 @@ internal bool IsJagged /// /// An array filled with the data provided in the serialized records. /// does not match the data from the payload. + /// + /// Check the total length of the array by using property before calling this method, + /// as an attacker could have sent you a small payload that will require to allocate a very large array + /// and potentially cause and Denial of Service. + /// [RequiresDynamicCode("The code for an array of the specified type might not be available.")] public Array GetArray(Type expectedArrayType, bool allowNulls = true) { diff --git a/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/NrbfDecoder.cs b/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/NrbfDecoder.cs index 76089c07ee0ce0..192fe80c6f5681 100644 --- a/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/NrbfDecoder.cs +++ b/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/NrbfDecoder.cs @@ -16,6 +16,10 @@ namespace System.Formats.Nrbf; /// /// Provides stateless methods for decoding .NET Remoting Binary Format (NRBF) encoded data. /// +/// +/// NrbfDecoder is an implementation of an NRBF reader, but its behaviors don't strictly follow BinaryFormatter's implementation. +/// Thus the output of NrbfDecoder shouldn't be used to determine whether a call to BinaryFormatter would be safe. +/// public static class NrbfDecoder { private static UTF8Encoding ThrowOnInvalidUtf8Encoding { get; } = new(false, throwOnInvalidBytes: true); diff --git a/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/SZArrayRecord.cs b/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/SZArrayRecord.cs index 0eef853a1e18a8..74359693604651 100644 --- a/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/SZArrayRecord.cs +++ b/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/SZArrayRecord.cs @@ -34,6 +34,11 @@ private protected SZArrayRecord(ArrayInfo arrayInfo) : base(arrayInfo) /// otherwise, . /// /// An array filled with the data provided in the serialized records. + /// + /// Check the total length of the array by using property before calling this method, + /// as an attacker could have sent you a small payload that will require to allocate a very large array + /// and potentially cause and Denial of Service. + /// public abstract T?[] GetArray(bool allowNulls = true); #pragma warning disable IL3051 // RequiresDynamicCode is not required in this particualar case diff --git a/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/SerializationRecord.cs b/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/SerializationRecord.cs index 43c51d2f864310..fa7d2eda86ca60 100644 --- a/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/SerializationRecord.cs +++ b/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/SerializationRecord.cs @@ -39,6 +39,10 @@ internal SerializationRecord() // others can't derive from this type /// Gets the name of the serialized type. /// /// The name of the serialized type. + /// + /// Since the provided type name may originate from untrusted input, + /// it should not be utilized for type loading, as it could potentially load a malicious type. + /// public abstract TypeName TypeName { get; } /// diff --git a/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/SerializationRecordId.cs b/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/SerializationRecordId.cs index a8318cb72d11de..fac6966f6abd49 100644 --- a/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/SerializationRecordId.cs +++ b/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/SerializationRecordId.cs @@ -16,6 +16,9 @@ namespace System.Formats.Nrbf; /// /// The ID of . /// +/// +/// It can be used the detect cycles in decoded records. +/// [DebuggerDisplay("{_id}")] public readonly struct SerializationRecordId : IEquatable { diff --git a/src/libraries/System.Linq/src/System/Linq/Select.cs b/src/libraries/System.Linq/src/System/Linq/Select.cs index 37a41c450f873c..ac27c8dda22eeb 100644 --- a/src/libraries/System.Linq/src/System/Linq/Select.cs +++ b/src/libraries/System.Linq/src/System/Linq/Select.cs @@ -25,7 +25,19 @@ public static IEnumerable Select( if (source is Iterator iterator) { - return iterator.Select(selector); + // With native AOT, calling into the `Select` generic virtual method results in NxM + // expansion of native code. If the option is enabled, we don't call the generic virtual + // for value types. We don't do the same for reference types because reference type + // expansion can happen lazily at runtime and the AOT compiler does postpone it (we + // don't need more code, just more data structures describing the new types). + if (IsSizeOptimized && typeof(TResult).IsValueType) + { + return new IEnumerableSelectIterator(iterator, selector); + } + else + { + return iterator.Select(selector); + } } if (source is IList ilist) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index f9ca22d194bc5f..0c11aacc65896e 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2789,4 +2789,4 @@ - + \ No newline at end of file diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln b/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln index 9be1cf583e2740..322f5bf805e6b1 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln @@ -1,4 +1,8 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35229.201 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.CoreLib", "..\..\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj", "{0F7BA062-C34C-41A8-840F-F0B074B18686}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}" @@ -73,16 +77,21 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{19EA33B4-0E8 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{1A04C5D7-1DE9-47C3-BCC1-147678B9085F}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{F81BA54D-38C5-4493-BB5C-344825706D3A}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{F81BA54D-38C5-4493-BB5C-344825706D3A}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{F0448246-E8A1-49FD-878C-7A6B8486A66B}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F0448246-E8A1-49FD-878C-7A6B8486A66B}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{9BB52E31-4890-4F4B-8B2B-6282EF13B8D2}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{9BB52E31-4890-4F4B-8B2B-6282EF13B8D2}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{7392B838-42AF-4F54-AD02-366397DAF640}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Checked|Any CPU = Checked|Any CPU + Checked|arm = Checked|arm + Checked|arm64 = Checked|arm64 + Checked|x64 = Checked|x64 + Checked|x86 = Checked|x86 Debug|Any CPU = Debug|Any CPU Debug|arm = Debug|arm Debug|arm64 = Debug|arm64 @@ -93,13 +102,18 @@ Global Release|arm64 = Release|arm64 Release|x64 = Release|x64 Release|x86 = Release|x86 - Checked|Any CPU = Checked|Any CPU - Checked|arm = Checked|arm - Checked|arm64 = Checked|arm64 - Checked|x64 = Checked|x64 - Checked|x86 = Checked|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|Any CPU.ActiveCfg = Checked|x64 + {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|Any CPU.Build.0 = Checked|x64 + {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|arm.ActiveCfg = Checked|arm + {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|arm.Build.0 = Checked|arm + {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|arm64.ActiveCfg = Checked|arm64 + {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|arm64.Build.0 = Checked|arm64 + {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|x64.ActiveCfg = Checked|x64 + {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|x64.Build.0 = Checked|x64 + {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|x86.ActiveCfg = Checked|x86 + {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|x86.Build.0 = Checked|x86 {0F7BA062-C34C-41A8-840F-F0B074B18686}.Debug|Any CPU.ActiveCfg = Debug|x64 {0F7BA062-C34C-41A8-840F-F0B074B18686}.Debug|Any CPU.Build.0 = Debug|x64 {0F7BA062-C34C-41A8-840F-F0B074B18686}.Debug|arm.ActiveCfg = Debug|arm @@ -120,16 +134,11 @@ Global {0F7BA062-C34C-41A8-840F-F0B074B18686}.Release|x64.Build.0 = Release|x64 {0F7BA062-C34C-41A8-840F-F0B074B18686}.Release|x86.ActiveCfg = Release|x86 {0F7BA062-C34C-41A8-840F-F0B074B18686}.Release|x86.Build.0 = Release|x86 - {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|Any CPU.ActiveCfg = Checked|x64 - {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|Any CPU.Build.0 = Checked|x64 - {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|arm.ActiveCfg = Checked|arm - {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|arm.Build.0 = Checked|arm - {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|arm64.ActiveCfg = Checked|arm64 - {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|arm64.Build.0 = Checked|arm64 - {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|x64.ActiveCfg = Checked|x64 - {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|x64.Build.0 = Checked|x64 - {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|x86.ActiveCfg = Checked|x86 - {0F7BA062-C34C-41A8-840F-F0B074B18686}.Checked|x86.Build.0 = Checked|x86 + {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Checked|arm.ActiveCfg = Debug|Any CPU + {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Checked|arm64.ActiveCfg = Debug|Any CPU + {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Checked|x64.ActiveCfg = Debug|Any CPU + {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Checked|x86.ActiveCfg = Debug|Any CPU {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Debug|Any CPU.Build.0 = Debug|Any CPU {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -146,11 +155,11 @@ Global {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Release|x64.Build.0 = Release|Any CPU {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Release|x86.ActiveCfg = Release|Any CPU {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Release|x86.Build.0 = Release|Any CPU - {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Checked|arm.ActiveCfg = Debug|Any CPU - {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Checked|arm64.ActiveCfg = Debug|Any CPU - {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Checked|x64.ActiveCfg = Debug|Any CPU - {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}.Checked|x86.ActiveCfg = Debug|Any CPU + {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Checked|arm.ActiveCfg = Debug|Any CPU + {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Checked|arm64.ActiveCfg = Debug|Any CPU + {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Checked|x64.ActiveCfg = Debug|Any CPU + {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Checked|x86.ActiveCfg = Debug|Any CPU {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Debug|Any CPU.Build.0 = Debug|Any CPU {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -167,11 +176,11 @@ Global {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Release|x64.Build.0 = Release|Any CPU {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Release|x86.ActiveCfg = Release|Any CPU {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Release|x86.Build.0 = Release|Any CPU - {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Checked|arm.ActiveCfg = Debug|Any CPU - {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Checked|arm64.ActiveCfg = Debug|Any CPU - {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Checked|x64.ActiveCfg = Debug|Any CPU - {FC1007CC-9E52-49B7-A47B-A8AE76E75986}.Checked|x86.ActiveCfg = Debug|Any CPU + {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Checked|arm.ActiveCfg = Debug|Any CPU + {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Checked|arm64.ActiveCfg = Debug|Any CPU + {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Checked|x64.ActiveCfg = Debug|Any CPU + {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Checked|x86.ActiveCfg = Debug|Any CPU {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Debug|Any CPU.Build.0 = Debug|Any CPU {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -188,11 +197,11 @@ Global {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Release|x64.Build.0 = Release|Any CPU {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Release|x86.ActiveCfg = Release|Any CPU {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Release|x86.Build.0 = Release|Any CPU - {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Checked|arm.ActiveCfg = Debug|Any CPU - {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Checked|arm64.ActiveCfg = Debug|Any CPU - {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Checked|x64.ActiveCfg = Debug|Any CPU - {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}.Checked|x86.ActiveCfg = Debug|Any CPU + {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Checked|arm.ActiveCfg = Debug|Any CPU + {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Checked|arm64.ActiveCfg = Debug|Any CPU + {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Checked|x64.ActiveCfg = Debug|Any CPU + {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Checked|x86.ActiveCfg = Debug|Any CPU {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Debug|Any CPU.Build.0 = Debug|Any CPU {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -209,11 +218,11 @@ Global {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Release|x64.Build.0 = Release|Any CPU {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Release|x86.ActiveCfg = Release|Any CPU {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Release|x86.Build.0 = Release|Any CPU - {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Checked|arm.ActiveCfg = Debug|Any CPU - {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Checked|arm64.ActiveCfg = Debug|Any CPU - {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Checked|x64.ActiveCfg = Debug|Any CPU - {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB}.Checked|x86.ActiveCfg = Debug|Any CPU + {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Checked|arm.ActiveCfg = Debug|Any CPU + {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Checked|arm64.ActiveCfg = Debug|Any CPU + {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Checked|x64.ActiveCfg = Debug|Any CPU + {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Checked|x86.ActiveCfg = Debug|Any CPU {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Debug|Any CPU.Build.0 = Debug|Any CPU {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -230,11 +239,11 @@ Global {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Release|x64.Build.0 = Release|Any CPU {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Release|x86.ActiveCfg = Release|Any CPU {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Release|x86.Build.0 = Release|Any CPU - {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Checked|arm.ActiveCfg = Debug|Any CPU - {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Checked|arm64.ActiveCfg = Debug|Any CPU - {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Checked|x64.ActiveCfg = Debug|Any CPU - {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8}.Checked|x86.ActiveCfg = Debug|Any CPU + {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Checked|arm.ActiveCfg = Debug|Any CPU + {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Checked|arm64.ActiveCfg = Debug|Any CPU + {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Checked|x64.ActiveCfg = Debug|Any CPU + {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Checked|x86.ActiveCfg = Debug|Any CPU {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Debug|Any CPU.Build.0 = Debug|Any CPU {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -251,11 +260,11 @@ Global {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Release|x64.Build.0 = Release|Any CPU {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Release|x86.ActiveCfg = Release|Any CPU {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Release|x86.Build.0 = Release|Any CPU - {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Checked|arm.ActiveCfg = Debug|Any CPU - {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Checked|arm64.ActiveCfg = Debug|Any CPU - {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Checked|x64.ActiveCfg = Debug|Any CPU - {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE}.Checked|x86.ActiveCfg = Debug|Any CPU + {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Checked|arm.ActiveCfg = Debug|Any CPU + {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Checked|arm64.ActiveCfg = Debug|Any CPU + {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Checked|x64.ActiveCfg = Debug|Any CPU + {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Checked|x86.ActiveCfg = Debug|Any CPU {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Debug|Any CPU.Build.0 = Debug|Any CPU {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -272,11 +281,11 @@ Global {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Release|x64.Build.0 = Release|Any CPU {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Release|x86.ActiveCfg = Release|Any CPU {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Release|x86.Build.0 = Release|Any CPU - {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Checked|arm.ActiveCfg = Debug|Any CPU - {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Checked|arm64.ActiveCfg = Debug|Any CPU - {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Checked|x64.ActiveCfg = Debug|Any CPU - {8CFB1155-26A2-43E3-B192-1F87D9E543AC}.Checked|x86.ActiveCfg = Debug|Any CPU + {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Checked|arm.ActiveCfg = Debug|Any CPU + {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Checked|arm64.ActiveCfg = Debug|Any CPU + {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Checked|x64.ActiveCfg = Debug|Any CPU + {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Checked|x86.ActiveCfg = Debug|Any CPU {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Debug|Any CPU.Build.0 = Debug|Any CPU {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -293,11 +302,11 @@ Global {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Release|x64.Build.0 = Release|Any CPU {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Release|x86.ActiveCfg = Release|Any CPU {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Release|x86.Build.0 = Release|Any CPU - {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Checked|arm.ActiveCfg = Debug|Any CPU - {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Checked|arm64.ActiveCfg = Debug|Any CPU - {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Checked|x64.ActiveCfg = Debug|Any CPU - {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08}.Checked|x86.ActiveCfg = Debug|Any CPU + {74143A5F-6987-4AB5-B786-DE358F01241B}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {74143A5F-6987-4AB5-B786-DE358F01241B}.Checked|arm.ActiveCfg = Debug|Any CPU + {74143A5F-6987-4AB5-B786-DE358F01241B}.Checked|arm64.ActiveCfg = Debug|Any CPU + {74143A5F-6987-4AB5-B786-DE358F01241B}.Checked|x64.ActiveCfg = Debug|Any CPU + {74143A5F-6987-4AB5-B786-DE358F01241B}.Checked|x86.ActiveCfg = Debug|Any CPU {74143A5F-6987-4AB5-B786-DE358F01241B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {74143A5F-6987-4AB5-B786-DE358F01241B}.Debug|Any CPU.Build.0 = Debug|Any CPU {74143A5F-6987-4AB5-B786-DE358F01241B}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -314,11 +323,11 @@ Global {74143A5F-6987-4AB5-B786-DE358F01241B}.Release|x64.Build.0 = Release|Any CPU {74143A5F-6987-4AB5-B786-DE358F01241B}.Release|x86.ActiveCfg = Release|Any CPU {74143A5F-6987-4AB5-B786-DE358F01241B}.Release|x86.Build.0 = Release|Any CPU - {74143A5F-6987-4AB5-B786-DE358F01241B}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {74143A5F-6987-4AB5-B786-DE358F01241B}.Checked|arm.ActiveCfg = Debug|Any CPU - {74143A5F-6987-4AB5-B786-DE358F01241B}.Checked|arm64.ActiveCfg = Debug|Any CPU - {74143A5F-6987-4AB5-B786-DE358F01241B}.Checked|x64.ActiveCfg = Debug|Any CPU - {74143A5F-6987-4AB5-B786-DE358F01241B}.Checked|x86.ActiveCfg = Debug|Any CPU + {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|arm.ActiveCfg = Debug|Any CPU + {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|arm64.ActiveCfg = Debug|Any CPU + {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|x64.ActiveCfg = Debug|Any CPU + {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|x86.ActiveCfg = Debug|Any CPU {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|Any CPU.Build.0 = Debug|Any CPU {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -335,11 +344,11 @@ Global {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|x64.Build.0 = Release|Any CPU {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|x86.ActiveCfg = Release|Any CPU {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Release|x86.Build.0 = Release|Any CPU - {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|arm.ActiveCfg = Debug|Any CPU - {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|arm64.ActiveCfg = Debug|Any CPU - {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|x64.ActiveCfg = Debug|Any CPU - {CE5E53C1-F9B5-41EE-8D00-837913EC57D1}.Checked|x86.ActiveCfg = Debug|Any CPU + {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Checked|arm.ActiveCfg = Debug|Any CPU + {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Checked|arm64.ActiveCfg = Debug|Any CPU + {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Checked|x64.ActiveCfg = Debug|Any CPU + {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Checked|x86.ActiveCfg = Debug|Any CPU {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Debug|Any CPU.Build.0 = Debug|Any CPU {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -356,11 +365,11 @@ Global {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Release|x64.Build.0 = Release|Any CPU {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Release|x86.ActiveCfg = Release|Any CPU {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Release|x86.Build.0 = Release|Any CPU - {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Checked|arm.ActiveCfg = Debug|Any CPU - {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Checked|arm64.ActiveCfg = Debug|Any CPU - {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Checked|x64.ActiveCfg = Debug|Any CPU - {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1}.Checked|x86.ActiveCfg = Debug|Any CPU + {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Checked|arm.ActiveCfg = Debug|Any CPU + {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Checked|arm64.ActiveCfg = Debug|Any CPU + {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Checked|x64.ActiveCfg = Debug|Any CPU + {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Checked|x86.ActiveCfg = Debug|Any CPU {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Debug|Any CPU.Build.0 = Debug|Any CPU {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -377,11 +386,11 @@ Global {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Release|x64.Build.0 = Release|Any CPU {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Release|x86.ActiveCfg = Release|Any CPU {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Release|x86.Build.0 = Release|Any CPU - {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Checked|arm.ActiveCfg = Debug|Any CPU - {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Checked|arm64.ActiveCfg = Debug|Any CPU - {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Checked|x64.ActiveCfg = Debug|Any CPU - {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45}.Checked|x86.ActiveCfg = Debug|Any CPU + {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Checked|arm.ActiveCfg = Debug|Any CPU + {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Checked|arm64.ActiveCfg = Debug|Any CPU + {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Checked|x64.ActiveCfg = Debug|Any CPU + {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Checked|x86.ActiveCfg = Debug|Any CPU {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -398,11 +407,11 @@ Global {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Release|x64.Build.0 = Release|Any CPU {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Release|x86.ActiveCfg = Release|Any CPU {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Release|x86.Build.0 = Release|Any CPU - {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Checked|arm.ActiveCfg = Debug|Any CPU - {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Checked|arm64.ActiveCfg = Debug|Any CPU - {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Checked|x64.ActiveCfg = Debug|Any CPU - {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9}.Checked|x86.ActiveCfg = Debug|Any CPU + {BFED925C-18F2-4C98-833E-66F205234598}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {BFED925C-18F2-4C98-833E-66F205234598}.Checked|arm.ActiveCfg = Debug|Any CPU + {BFED925C-18F2-4C98-833E-66F205234598}.Checked|arm64.ActiveCfg = Debug|Any CPU + {BFED925C-18F2-4C98-833E-66F205234598}.Checked|x64.ActiveCfg = Debug|Any CPU + {BFED925C-18F2-4C98-833E-66F205234598}.Checked|x86.ActiveCfg = Debug|Any CPU {BFED925C-18F2-4C98-833E-66F205234598}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BFED925C-18F2-4C98-833E-66F205234598}.Debug|Any CPU.Build.0 = Debug|Any CPU {BFED925C-18F2-4C98-833E-66F205234598}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -419,11 +428,11 @@ Global {BFED925C-18F2-4C98-833E-66F205234598}.Release|x64.Build.0 = Release|Any CPU {BFED925C-18F2-4C98-833E-66F205234598}.Release|x86.ActiveCfg = Release|Any CPU {BFED925C-18F2-4C98-833E-66F205234598}.Release|x86.Build.0 = Release|Any CPU - {BFED925C-18F2-4C98-833E-66F205234598}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {BFED925C-18F2-4C98-833E-66F205234598}.Checked|arm.ActiveCfg = Debug|Any CPU - {BFED925C-18F2-4C98-833E-66F205234598}.Checked|arm64.ActiveCfg = Debug|Any CPU - {BFED925C-18F2-4C98-833E-66F205234598}.Checked|x64.ActiveCfg = Debug|Any CPU - {BFED925C-18F2-4C98-833E-66F205234598}.Checked|x86.ActiveCfg = Debug|Any CPU + {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|arm.ActiveCfg = Debug|Any CPU + {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|arm64.ActiveCfg = Debug|Any CPU + {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|x64.ActiveCfg = Debug|Any CPU + {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|x86.ActiveCfg = Debug|Any CPU {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|Any CPU.Build.0 = Debug|Any CPU {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -440,11 +449,11 @@ Global {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|x64.Build.0 = Release|Any CPU {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|x86.ActiveCfg = Release|Any CPU {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Release|x86.Build.0 = Release|Any CPU - {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|arm.ActiveCfg = Debug|Any CPU - {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|arm64.ActiveCfg = Debug|Any CPU - {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|x64.ActiveCfg = Debug|Any CPU - {765B4AA5-723A-44FF-BC4E-EB0F03103F6D}.Checked|x86.ActiveCfg = Debug|Any CPU + {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Checked|arm.ActiveCfg = Debug|Any CPU + {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Checked|arm64.ActiveCfg = Debug|Any CPU + {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Checked|x64.ActiveCfg = Debug|Any CPU + {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Checked|x86.ActiveCfg = Debug|Any CPU {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Debug|Any CPU.Build.0 = Debug|Any CPU {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -461,11 +470,11 @@ Global {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Release|x64.Build.0 = Release|Any CPU {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Release|x86.ActiveCfg = Release|Any CPU {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Release|x86.Build.0 = Release|Any CPU - {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Checked|arm.ActiveCfg = Debug|Any CPU - {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Checked|arm64.ActiveCfg = Debug|Any CPU - {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Checked|x64.ActiveCfg = Debug|Any CPU - {DE207B2C-A0CC-47C8-AC20-46A8C0970287}.Checked|x86.ActiveCfg = Debug|Any CPU + {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Checked|arm.ActiveCfg = Debug|Any CPU + {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Checked|arm64.ActiveCfg = Debug|Any CPU + {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Checked|x64.ActiveCfg = Debug|Any CPU + {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Checked|x86.ActiveCfg = Debug|Any CPU {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Debug|Any CPU.Build.0 = Debug|Any CPU {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -482,11 +491,11 @@ Global {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Release|x64.Build.0 = Release|Any CPU {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Release|x86.ActiveCfg = Release|Any CPU {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Release|x86.Build.0 = Release|Any CPU - {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Checked|arm.ActiveCfg = Debug|Any CPU - {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Checked|arm64.ActiveCfg = Debug|Any CPU - {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Checked|x64.ActiveCfg = Debug|Any CPU - {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD}.Checked|x86.ActiveCfg = Debug|Any CPU + {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Checked|arm.ActiveCfg = Debug|Any CPU + {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Checked|arm64.ActiveCfg = Debug|Any CPU + {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Checked|x64.ActiveCfg = Debug|Any CPU + {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Checked|x86.ActiveCfg = Debug|Any CPU {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Debug|Any CPU.Build.0 = Debug|Any CPU {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -503,11 +512,11 @@ Global {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Release|x64.Build.0 = Release|Any CPU {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Release|x86.ActiveCfg = Release|Any CPU {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Release|x86.Build.0 = Release|Any CPU - {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Checked|arm.ActiveCfg = Debug|Any CPU - {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Checked|arm64.ActiveCfg = Debug|Any CPU - {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Checked|x64.ActiveCfg = Debug|Any CPU - {B4E3E774-2C16-4CBF-87EF-88C547529B94}.Checked|x86.ActiveCfg = Debug|Any CPU + {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Checked|arm.ActiveCfg = Debug|Any CPU + {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Checked|arm64.ActiveCfg = Debug|Any CPU + {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Checked|x64.ActiveCfg = Debug|Any CPU + {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Checked|x86.ActiveCfg = Debug|Any CPU {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Debug|Any CPU.Build.0 = Debug|Any CPU {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -524,11 +533,11 @@ Global {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Release|x64.Build.0 = Release|Any CPU {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Release|x86.ActiveCfg = Release|Any CPU {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Release|x86.Build.0 = Release|Any CPU - {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Checked|arm.ActiveCfg = Debug|Any CPU - {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Checked|arm64.ActiveCfg = Debug|Any CPU - {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Checked|x64.ActiveCfg = Debug|Any CPU - {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532}.Checked|x86.ActiveCfg = Debug|Any CPU + {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Checked|arm.ActiveCfg = Debug|Any CPU + {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Checked|arm64.ActiveCfg = Debug|Any CPU + {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Checked|x64.ActiveCfg = Debug|Any CPU + {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Checked|x86.ActiveCfg = Debug|Any CPU {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Debug|Any CPU.Build.0 = Debug|Any CPU {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -545,11 +554,11 @@ Global {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Release|x64.Build.0 = Release|Any CPU {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Release|x86.ActiveCfg = Release|Any CPU {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Release|x86.Build.0 = Release|Any CPU - {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Checked|arm.ActiveCfg = Debug|Any CPU - {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Checked|arm64.ActiveCfg = Debug|Any CPU - {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Checked|x64.ActiveCfg = Debug|Any CPU - {44BAE6F1-94C2-415B-9A16-3B8EC429B09B}.Checked|x86.ActiveCfg = Debug|Any CPU + {1EB2EBE2-12EA-4545-B390-098F083329A1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {1EB2EBE2-12EA-4545-B390-098F083329A1}.Checked|arm.ActiveCfg = Debug|Any CPU + {1EB2EBE2-12EA-4545-B390-098F083329A1}.Checked|arm64.ActiveCfg = Debug|Any CPU + {1EB2EBE2-12EA-4545-B390-098F083329A1}.Checked|x64.ActiveCfg = Debug|Any CPU + {1EB2EBE2-12EA-4545-B390-098F083329A1}.Checked|x86.ActiveCfg = Debug|Any CPU {1EB2EBE2-12EA-4545-B390-098F083329A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1EB2EBE2-12EA-4545-B390-098F083329A1}.Debug|Any CPU.Build.0 = Debug|Any CPU {1EB2EBE2-12EA-4545-B390-098F083329A1}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -566,11 +575,11 @@ Global {1EB2EBE2-12EA-4545-B390-098F083329A1}.Release|x64.Build.0 = Release|Any CPU {1EB2EBE2-12EA-4545-B390-098F083329A1}.Release|x86.ActiveCfg = Release|Any CPU {1EB2EBE2-12EA-4545-B390-098F083329A1}.Release|x86.Build.0 = Release|Any CPU - {1EB2EBE2-12EA-4545-B390-098F083329A1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {1EB2EBE2-12EA-4545-B390-098F083329A1}.Checked|arm.ActiveCfg = Debug|Any CPU - {1EB2EBE2-12EA-4545-B390-098F083329A1}.Checked|arm64.ActiveCfg = Debug|Any CPU - {1EB2EBE2-12EA-4545-B390-098F083329A1}.Checked|x64.ActiveCfg = Debug|Any CPU - {1EB2EBE2-12EA-4545-B390-098F083329A1}.Checked|x86.ActiveCfg = Debug|Any CPU + {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Checked|arm.ActiveCfg = Debug|Any CPU + {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Checked|arm64.ActiveCfg = Debug|Any CPU + {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Checked|x64.ActiveCfg = Debug|Any CPU + {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Checked|x86.ActiveCfg = Debug|Any CPU {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Debug|Any CPU.Build.0 = Debug|Any CPU {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -587,11 +596,11 @@ Global {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Release|x64.Build.0 = Release|Any CPU {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Release|x86.ActiveCfg = Release|Any CPU {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Release|x86.Build.0 = Release|Any CPU - {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Checked|arm.ActiveCfg = Debug|Any CPU - {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Checked|arm64.ActiveCfg = Debug|Any CPU - {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Checked|x64.ActiveCfg = Debug|Any CPU - {B18C5A3A-CAB0-4B62-9C01-7A046E05089F}.Checked|x86.ActiveCfg = Debug|Any CPU + {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Checked|arm.ActiveCfg = Debug|Any CPU + {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Checked|arm64.ActiveCfg = Debug|Any CPU + {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Checked|x64.ActiveCfg = Debug|Any CPU + {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Checked|x86.ActiveCfg = Debug|Any CPU {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Debug|Any CPU.Build.0 = Debug|Any CPU {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -608,11 +617,11 @@ Global {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Release|x64.Build.0 = Release|Any CPU {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Release|x86.ActiveCfg = Release|Any CPU {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Release|x86.Build.0 = Release|Any CPU - {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Checked|arm.ActiveCfg = Debug|Any CPU - {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Checked|arm64.ActiveCfg = Debug|Any CPU - {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Checked|x64.ActiveCfg = Debug|Any CPU - {04A40E6C-DD10-473D-AFF8-9033E936DC46}.Checked|x86.ActiveCfg = Debug|Any CPU + {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Checked|arm.ActiveCfg = Debug|Any CPU + {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Checked|arm64.ActiveCfg = Debug|Any CPU + {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Checked|x64.ActiveCfg = Debug|Any CPU + {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Checked|x86.ActiveCfg = Debug|Any CPU {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Debug|Any CPU.Build.0 = Debug|Any CPU {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -629,11 +638,11 @@ Global {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Release|x64.Build.0 = Release|Any CPU {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Release|x86.ActiveCfg = Release|Any CPU {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Release|x86.Build.0 = Release|Any CPU - {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Checked|arm.ActiveCfg = Debug|Any CPU - {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Checked|arm64.ActiveCfg = Debug|Any CPU - {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Checked|x64.ActiveCfg = Debug|Any CPU - {B86E4599-88CF-4662-96BA-3FCB926D0BA1}.Checked|x86.ActiveCfg = Debug|Any CPU + {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Checked|arm.ActiveCfg = Debug|Any CPU + {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Checked|arm64.ActiveCfg = Debug|Any CPU + {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Checked|x64.ActiveCfg = Debug|Any CPU + {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Checked|x86.ActiveCfg = Debug|Any CPU {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Debug|Any CPU.Build.0 = Debug|Any CPU {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -650,11 +659,11 @@ Global {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Release|x64.Build.0 = Release|Any CPU {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Release|x86.ActiveCfg = Release|Any CPU {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Release|x86.Build.0 = Release|Any CPU - {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Checked|arm.ActiveCfg = Debug|Any CPU - {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Checked|arm64.ActiveCfg = Debug|Any CPU - {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Checked|x64.ActiveCfg = Debug|Any CPU - {424A7718-F5B7-41FB-A74B-1E23A9BF13EA}.Checked|x86.ActiveCfg = Debug|Any CPU + {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Checked|arm.ActiveCfg = Debug|Any CPU + {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Checked|arm64.ActiveCfg = Debug|Any CPU + {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Checked|x64.ActiveCfg = Debug|Any CPU + {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Checked|x86.ActiveCfg = Debug|Any CPU {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Debug|Any CPU.Build.0 = Debug|Any CPU {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -671,11 +680,11 @@ Global {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Release|x64.Build.0 = Release|Any CPU {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Release|x86.ActiveCfg = Release|Any CPU {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Release|x86.Build.0 = Release|Any CPU - {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Checked|arm.ActiveCfg = Debug|Any CPU - {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Checked|arm64.ActiveCfg = Debug|Any CPU - {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Checked|x64.ActiveCfg = Debug|Any CPU - {D620CBA3-326B-4E4A-81C2-3D9E9258E45C}.Checked|x86.ActiveCfg = Debug|Any CPU + {8683B814-5459-4412-A881-ECAFF9ED1781}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {8683B814-5459-4412-A881-ECAFF9ED1781}.Checked|arm.ActiveCfg = Debug|Any CPU + {8683B814-5459-4412-A881-ECAFF9ED1781}.Checked|arm64.ActiveCfg = Debug|Any CPU + {8683B814-5459-4412-A881-ECAFF9ED1781}.Checked|x64.ActiveCfg = Debug|Any CPU + {8683B814-5459-4412-A881-ECAFF9ED1781}.Checked|x86.ActiveCfg = Debug|Any CPU {8683B814-5459-4412-A881-ECAFF9ED1781}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8683B814-5459-4412-A881-ECAFF9ED1781}.Debug|Any CPU.Build.0 = Debug|Any CPU {8683B814-5459-4412-A881-ECAFF9ED1781}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -692,11 +701,11 @@ Global {8683B814-5459-4412-A881-ECAFF9ED1781}.Release|x64.Build.0 = Release|Any CPU {8683B814-5459-4412-A881-ECAFF9ED1781}.Release|x86.ActiveCfg = Release|Any CPU {8683B814-5459-4412-A881-ECAFF9ED1781}.Release|x86.Build.0 = Release|Any CPU - {8683B814-5459-4412-A881-ECAFF9ED1781}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {8683B814-5459-4412-A881-ECAFF9ED1781}.Checked|arm.ActiveCfg = Debug|Any CPU - {8683B814-5459-4412-A881-ECAFF9ED1781}.Checked|arm64.ActiveCfg = Debug|Any CPU - {8683B814-5459-4412-A881-ECAFF9ED1781}.Checked|x64.ActiveCfg = Debug|Any CPU - {8683B814-5459-4412-A881-ECAFF9ED1781}.Checked|x86.ActiveCfg = Debug|Any CPU + {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Checked|arm.ActiveCfg = Debug|Any CPU + {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Checked|arm64.ActiveCfg = Debug|Any CPU + {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Checked|x64.ActiveCfg = Debug|Any CPU + {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Checked|x86.ActiveCfg = Debug|Any CPU {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Debug|Any CPU.Build.0 = Debug|Any CPU {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -713,11 +722,11 @@ Global {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Release|x64.Build.0 = Release|Any CPU {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Release|x86.ActiveCfg = Release|Any CPU {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Release|x86.Build.0 = Release|Any CPU - {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Checked|arm.ActiveCfg = Debug|Any CPU - {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Checked|arm64.ActiveCfg = Debug|Any CPU - {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Checked|x64.ActiveCfg = Debug|Any CPU - {4D8B7538-D933-4F3A-818D-4E19ABA7E182}.Checked|x86.ActiveCfg = Debug|Any CPU + {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Checked|arm.ActiveCfg = Debug|Any CPU + {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Checked|arm64.ActiveCfg = Debug|Any CPU + {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Checked|x64.ActiveCfg = Debug|Any CPU + {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Checked|x86.ActiveCfg = Debug|Any CPU {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Debug|Any CPU.Build.0 = Debug|Any CPU {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -734,11 +743,11 @@ Global {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Release|x64.Build.0 = Release|Any CPU {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Release|x86.ActiveCfg = Release|Any CPU {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Release|x86.Build.0 = Release|Any CPU - {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Checked|arm.ActiveCfg = Debug|Any CPU - {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Checked|arm64.ActiveCfg = Debug|Any CPU - {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Checked|x64.ActiveCfg = Debug|Any CPU - {6C60944F-4FE1-450F-884B-D523EDFCFAB3}.Checked|x86.ActiveCfg = Debug|Any CPU + {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Checked|arm.ActiveCfg = Debug|Any CPU + {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Checked|arm64.ActiveCfg = Debug|Any CPU + {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Checked|x64.ActiveCfg = Debug|Any CPU + {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Checked|x86.ActiveCfg = Debug|Any CPU {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Debug|Any CPU.Build.0 = Debug|Any CPU {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -755,11 +764,11 @@ Global {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Release|x64.Build.0 = Release|Any CPU {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Release|x86.ActiveCfg = Release|Any CPU {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Release|x86.Build.0 = Release|Any CPU - {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Checked|arm.ActiveCfg = Debug|Any CPU - {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Checked|arm64.ActiveCfg = Debug|Any CPU - {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Checked|x64.ActiveCfg = Debug|Any CPU - {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B}.Checked|x86.ActiveCfg = Debug|Any CPU + {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Checked|arm.ActiveCfg = Debug|Any CPU + {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Checked|arm64.ActiveCfg = Debug|Any CPU + {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Checked|x64.ActiveCfg = Debug|Any CPU + {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Checked|x86.ActiveCfg = Debug|Any CPU {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Debug|Any CPU.Build.0 = Debug|Any CPU {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -776,11 +785,11 @@ Global {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Release|x64.Build.0 = Release|Any CPU {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Release|x86.ActiveCfg = Release|Any CPU {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Release|x86.Build.0 = Release|Any CPU - {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Checked|arm.ActiveCfg = Debug|Any CPU - {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Checked|arm64.ActiveCfg = Debug|Any CPU - {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Checked|x64.ActiveCfg = Debug|Any CPU - {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A}.Checked|x86.ActiveCfg = Debug|Any CPU + {008873D5-9028-4FF3-8354-71F713748625}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {008873D5-9028-4FF3-8354-71F713748625}.Checked|arm.ActiveCfg = Debug|Any CPU + {008873D5-9028-4FF3-8354-71F713748625}.Checked|arm64.ActiveCfg = Debug|Any CPU + {008873D5-9028-4FF3-8354-71F713748625}.Checked|x64.ActiveCfg = Debug|Any CPU + {008873D5-9028-4FF3-8354-71F713748625}.Checked|x86.ActiveCfg = Debug|Any CPU {008873D5-9028-4FF3-8354-71F713748625}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {008873D5-9028-4FF3-8354-71F713748625}.Debug|Any CPU.Build.0 = Debug|Any CPU {008873D5-9028-4FF3-8354-71F713748625}.Debug|arm.ActiveCfg = Debug|Any CPU @@ -797,54 +806,54 @@ Global {008873D5-9028-4FF3-8354-71F713748625}.Release|x64.Build.0 = Release|Any CPU {008873D5-9028-4FF3-8354-71F713748625}.Release|x86.ActiveCfg = Release|Any CPU {008873D5-9028-4FF3-8354-71F713748625}.Release|x86.Build.0 = Release|Any CPU - {008873D5-9028-4FF3-8354-71F713748625}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {008873D5-9028-4FF3-8354-71F713748625}.Checked|arm.ActiveCfg = Debug|Any CPU - {008873D5-9028-4FF3-8354-71F713748625}.Checked|arm64.ActiveCfg = Debug|Any CPU - {008873D5-9028-4FF3-8354-71F713748625}.Checked|x64.ActiveCfg = Debug|Any CPU - {008873D5-9028-4FF3-8354-71F713748625}.Checked|x86.ActiveCfg = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {0F7BA062-C34C-41A8-840F-F0B074B18686} = {32733782-56D6-4EAF-B94E-5D10C759DD57} + {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C} {FC1007CC-9E52-49B7-A47B-A8AE76E75986} = {32733782-56D6-4EAF-B94E-5D10C759DD57} + {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3} = {19EA33B4-0E87-451F-95E3-8F3959117654} {B79E5BB4-2595-48BC-A44C-0A7949AFBDEB} = {32733782-56D6-4EAF-B94E-5D10C759DD57} + {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8} = {19EA33B4-0E87-451F-95E3-8F3959117654} {C1C606F3-A246-4EA0-A467-3AC4F31C2AFE} = {32733782-56D6-4EAF-B94E-5D10C759DD57} + {8CFB1155-26A2-43E3-B192-1F87D9E543AC} = {1A04C5D7-1DE9-47C3-BCC1-147678B9085F} + {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08} = {19EA33B4-0E87-451F-95E3-8F3959117654} {74143A5F-6987-4AB5-B786-DE358F01241B} = {32733782-56D6-4EAF-B94E-5D10C759DD57} + {CE5E53C1-F9B5-41EE-8D00-837913EC57D1} = {1A04C5D7-1DE9-47C3-BCC1-147678B9085F} + {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1} = {19EA33B4-0E87-451F-95E3-8F3959117654} {71A845ED-4344-41FC-8FCA-3AC9B6BA6C45} = {32733782-56D6-4EAF-B94E-5D10C759DD57} - {1EB2EBE2-12EA-4545-B390-098F083329A1} = {32733782-56D6-4EAF-B94E-5D10C759DD57} - {04A40E6C-DD10-473D-AFF8-9033E936DC46} = {32733782-56D6-4EAF-B94E-5D10C759DD57} - {424A7718-F5B7-41FB-A74B-1E23A9BF13EA} = {32733782-56D6-4EAF-B94E-5D10C759DD57} - {8683B814-5459-4412-A881-ECAFF9ED1781} = {32733782-56D6-4EAF-B94E-5D10C759DD57} - {ED86AB26-1CFB-457D-BF87-B7A0D8FAF272} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C} {E6A30001-84E3-4C7A-9B56-B9DEA71B3CF9} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C} {BFED925C-18F2-4C98-833E-66F205234598} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C} {765B4AA5-723A-44FF-BC4E-EB0F03103F6D} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C} + {DE207B2C-A0CC-47C8-AC20-46A8C0970287} = {1A04C5D7-1DE9-47C3-BCC1-147678B9085F} + {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD} = {1A04C5D7-1DE9-47C3-BCC1-147678B9085F} + {B4E3E774-2C16-4CBF-87EF-88C547529B94} = {1A04C5D7-1DE9-47C3-BCC1-147678B9085F} {EC3ADEFA-1FF3-482C-8CCB-FE4C77292532} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C} - {8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3} = {19EA33B4-0E87-451F-95E3-8F3959117654} - {E00AE8BB-7C7F-4D07-949D-EDCC815AC8C8} = {19EA33B4-0E87-451F-95E3-8F3959117654} - {D549C13B-FC0D-4B5A-B50D-8F74CB5A3D08} = {19EA33B4-0E87-451F-95E3-8F3959117654} - {28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1} = {19EA33B4-0E87-451F-95E3-8F3959117654} {44BAE6F1-94C2-415B-9A16-3B8EC429B09B} = {19EA33B4-0E87-451F-95E3-8F3959117654} + {1EB2EBE2-12EA-4545-B390-098F083329A1} = {32733782-56D6-4EAF-B94E-5D10C759DD57} {B18C5A3A-CAB0-4B62-9C01-7A046E05089F} = {19EA33B4-0E87-451F-95E3-8F3959117654} + {04A40E6C-DD10-473D-AFF8-9033E936DC46} = {32733782-56D6-4EAF-B94E-5D10C759DD57} {B86E4599-88CF-4662-96BA-3FCB926D0BA1} = {19EA33B4-0E87-451F-95E3-8F3959117654} + {424A7718-F5B7-41FB-A74B-1E23A9BF13EA} = {32733782-56D6-4EAF-B94E-5D10C759DD57} {D620CBA3-326B-4E4A-81C2-3D9E9258E45C} = {19EA33B4-0E87-451F-95E3-8F3959117654} - {8CFB1155-26A2-43E3-B192-1F87D9E543AC} = {1A04C5D7-1DE9-47C3-BCC1-147678B9085F} - {CE5E53C1-F9B5-41EE-8D00-837913EC57D1} = {1A04C5D7-1DE9-47C3-BCC1-147678B9085F} - {DE207B2C-A0CC-47C8-AC20-46A8C0970287} = {1A04C5D7-1DE9-47C3-BCC1-147678B9085F} - {09AA6758-0BD3-4312-9C07-AE9F1D50A3AD} = {1A04C5D7-1DE9-47C3-BCC1-147678B9085F} - {B4E3E774-2C16-4CBF-87EF-88C547529B94} = {1A04C5D7-1DE9-47C3-BCC1-147678B9085F} + {8683B814-5459-4412-A881-ECAFF9ED1781} = {32733782-56D6-4EAF-B94E-5D10C759DD57} {4D8B7538-D933-4F3A-818D-4E19ABA7E182} = {F81BA54D-38C5-4493-BB5C-344825706D3A} {6C60944F-4FE1-450F-884B-D523EDFCFAB3} = {F81BA54D-38C5-4493-BB5C-344825706D3A} - {F81BA54D-38C5-4493-BB5C-344825706D3A} = {7392B838-42AF-4F54-AD02-366397DAF640} {B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B} = {F0448246-E8A1-49FD-878C-7A6B8486A66B} {42F9A600-BEC3-4F87-97EE-38E0DCAABC5A} = {F0448246-E8A1-49FD-878C-7A6B8486A66B} - {F0448246-E8A1-49FD-878C-7A6B8486A66B} = {7392B838-42AF-4F54-AD02-366397DAF640} {008873D5-9028-4FF3-8354-71F713748625} = {9BB52E31-4890-4F4B-8B2B-6282EF13B8D2} + {F81BA54D-38C5-4493-BB5C-344825706D3A} = {7392B838-42AF-4F54-AD02-366397DAF640} + {F0448246-E8A1-49FD-878C-7A6B8486A66B} = {7392B838-42AF-4F54-AD02-366397DAF640} {9BB52E31-4890-4F4B-8B2B-6282EF13B8D2} = {7392B838-42AF-4F54-AD02-366397DAF640} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3FE64246-4AFA-424A-AE5D-7007E20451B5} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\System.Private.CoreLib\src\System.Private.CoreLib.Shared.projitems*{0f7ba062-c34c-41a8-840f-f0b074b18686}*SharedItemsImports = 5 + ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{42f9a600-bec3-4f87-97ee-38e0dcaabc5a}*SharedItemsImports = 5 + ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{6c60944f-4fe1-450f-884b-d523edfcfab3}*SharedItemsImports = 5 + EndGlobalSection EndGlobal diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Constants.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Constants.cs index a5475e27c70a46..5dd5fa98ceb1b4 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Constants.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Constants.cs @@ -14,6 +14,7 @@ internal static class Constants public const string JSFunctionSignatureGlobal = "global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding"; public const string JSMarshalerArgumentGlobal = "global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument"; + public const string JSMarshalerArgument = "System.Runtime.InteropServices.JavaScript.JSMarshalerArgument"; public const string ModuleInitializerAttributeGlobal = "global::System.Runtime.CompilerServices.ModuleInitializerAttribute"; public const string CompilerGeneratedAttributeGlobal = "global::System.Runtime.CompilerServices.CompilerGeneratedAttribute"; public const string DynamicDependencyAttributeGlobal = "global::System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute"; diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportCodeGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportCodeGenerator.cs deleted file mode 100644 index 33b08510d4fe8a..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportCodeGenerator.cs +++ /dev/null @@ -1,236 +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.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; - -namespace Microsoft.Interop.JavaScript -{ - internal sealed class JSExportCodeGenerator : JSCodeGenerator - { - private readonly BoundGenerators _marshallers; - - private readonly StubIdentifierContext _context; - private readonly JSExportData _jsExportData; - private readonly JSSignatureContext _signatureContext; - - public JSExportCodeGenerator( - ImmutableArray argTypes, - JSExportData attributeData, - JSSignatureContext signatureContext, - GeneratorDiagnosticsBag diagnosticsBag, - IMarshallingGeneratorResolver generatorResolver) - { - _signatureContext = signatureContext; - _jsExportData = attributeData; - - _marshallers = BoundGenerators.Create(argTypes, generatorResolver, StubCodeContext.DefaultNativeToManagedStub, new EmptyJSGenerator(), out var bindingFailures); - - diagnosticsBag.ReportGeneratorDiagnostics(bindingFailures); - - if (_marshallers.ManagedReturnMarshaller.UsesNativeIdentifier) - { - // If we need a different native return identifier, then recreate the context with the correct identifier before we generate any code. - _context = new DefaultIdentifierContext(ReturnIdentifier, ReturnNativeIdentifier, MarshalDirection.UnmanagedToManaged) - { - CodeEmitOptions = new(SkipInit: true), - }; - } - else - { - _context = new DefaultIdentifierContext(ReturnIdentifier, ReturnIdentifier, MarshalDirection.UnmanagedToManaged) - { - CodeEmitOptions = new(SkipInit: true), - }; - } - - // validate task + span mix - if (_marshallers.ManagedReturnMarshaller.TypeInfo.MarshallingAttributeInfo is JSMarshallingInfo(_, JSTaskTypeInfo)) - { - IBoundMarshallingGenerator spanArg = _marshallers.SignatureMarshallers.FirstOrDefault(m => m.TypeInfo.MarshallingAttributeInfo is JSMarshallingInfo(_, JSSpanTypeInfo)); - if (spanArg != default) - { - diagnosticsBag.ReportGeneratorDiagnostic(new GeneratorDiagnostic.NotSupported(spanArg.TypeInfo) - { - NotSupportedDetails = SR.SpanAndTaskNotSupported - }); - } - } - } - - public BlockSyntax GenerateJSExportBody() - { - List invoke = InvokeSyntax(); - GeneratedStatements statements = GeneratedStatements.Create(_marshallers, _context); - bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.CleanupCallerAllocated.IsEmpty || !statements.CleanupCalleeAllocated.IsEmpty; - VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForUnmanagedToManaged(_marshallers, _context, shouldInitializeVariables); - - var setupStatements = new List(); - SetupSyntax(setupStatements); - - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) - { - setupStatements.Add(SyntaxFactoryExtensions.Declare(PredefinedType(Token(SyntaxKind.BoolKeyword)), InvokeSucceededIdentifier, initializeToDefault: true)); - } - - setupStatements.AddRange(declarations.Initializations); - setupStatements.AddRange(declarations.Variables); - setupStatements.AddRange(statements.Setup); - - var tryStatements = new List(); - tryStatements.AddRange(statements.Unmarshal); - - tryStatements.AddRange(invoke); - - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) - { - tryStatements.Add(ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, - IdentifierName(InvokeSucceededIdentifier), - LiteralExpression(SyntaxKind.TrueLiteralExpression)))); - } - - tryStatements.AddRange(statements.NotifyForSuccessfulInvoke); - tryStatements.AddRange(statements.PinnedMarshal); - tryStatements.AddRange(statements.Marshal); - - List allStatements = setupStatements; - - // Wrap unmarshall, invocation and return value marshalling in try-catch. - // In case of exception, marshal exception instead of return value. - var tryInvokeAndMarshal = TryStatement(SingletonList(CatchClause() - .WithDeclaration(CatchDeclaration(IdentifierName(Constants.ExceptionGlobal)).WithIdentifier(Identifier("ex"))) - .WithBlock(Block(SingletonList( - ExpressionStatement(InvocationExpression( - MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(Constants.ArgumentException), IdentifierName(Constants.ToJSMethod))) - .WithArgumentList(ArgumentList(SingletonSeparatedList(Argument(IdentifierName("ex"))))))))))) - .WithBlock(Block(tryStatements)); - - List finallyStatements = new List(); - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) - { - finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal.Concat(statements.CleanupCalleeAllocated)))); - } - - finallyStatements.AddRange(statements.CleanupCallerAllocated); - - if (finallyStatements.Count > 0) - { - tryInvokeAndMarshal = TryStatement(Block(tryInvokeAndMarshal), default, FinallyClause(Block(finallyStatements))); - } - - allStatements.Add(tryInvokeAndMarshal); - - return Block(allStatements); - } - - public static StatementSyntax[] GenerateJSExportArchitectureCheck() - { - return new StatementSyntax[]{ - IfStatement( - BinaryExpression(SyntaxKind.LogicalOrExpression, - IdentifierName("initialized"), - BinaryExpression(SyntaxKind.NotEqualsExpression, - IdentifierName(Constants.OSArchitectureGlobal), - IdentifierName(Constants.ArchitectureWasmGlobal))), - ReturnStatement()), - ExpressionStatement( - AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, - IdentifierName("initialized"), - LiteralExpression(SyntaxKind.TrueLiteralExpression))), - }; - } - - public StatementSyntax GenerateJSExportRegistration() - { - var signatureArgs = new List - { - Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(_signatureContext.QualifiedMethodName))), - Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(_signatureContext.TypesHash))), - CreateSignaturesSyntax() - }; - - return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(Constants.JSFunctionSignatureGlobal), IdentifierName(Constants.BindCSFunctionMethod))) - .WithArgumentList(ArgumentList(SeparatedList(signatureArgs)))); - } - - private ArgumentSyntax CreateSignaturesSyntax() - { - IEnumerable types = _marshallers.ManagedReturnMarshaller is IJSMarshallingGenerator jsGen ? jsGen.GenerateBind() : []; - types = types - .Concat(_marshallers.NativeParameterMarshallers.OfType().SelectMany(p => p.GenerateBind())); - - return Argument(ArrayCreationExpression(ArrayType(IdentifierName(Constants.JSMarshalerTypeGlobal)) - .WithRankSpecifiers(SingletonList(ArrayRankSpecifier(SingletonSeparatedList(OmittedArraySizeExpression()))))) - .WithInitializer(InitializerExpression(SyntaxKind.ArrayInitializerExpression, SeparatedList(types)))); - } - - private void SetupSyntax(List statementsToUpdate) - { - foreach (IBoundMarshallingGenerator marshaller in _marshallers.NativeParameterMarshallers) - { - statementsToUpdate.Add(LocalDeclarationStatement(VariableDeclaration(marshaller.TypeInfo.ManagedType.Syntax) - .WithVariables(SingletonSeparatedList(VariableDeclarator(marshaller.TypeInfo.InstanceIdentifier))))); - } - - statementsToUpdate.Add(LocalDeclarationStatement(VariableDeclaration(RefType(IdentifierName(Constants.JSMarshalerArgumentGlobal))) - .WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier(Constants.ArgumentException)) - .WithInitializer(EqualsValueClause(RefExpression(ElementAccessExpression(IdentifierName(Constants.ArgumentsBuffer)) - .WithArgumentList(BracketedArgumentList(SingletonSeparatedList( - Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0))))))))))))); - - statementsToUpdate.Add(LocalDeclarationStatement(VariableDeclaration(RefType(IdentifierName(Constants.JSMarshalerArgumentGlobal))) - .WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier(Constants.ArgumentReturn)) - .WithInitializer(EqualsValueClause(RefExpression(ElementAccessExpression(IdentifierName(Constants.ArgumentsBuffer)) - .WithArgumentList(BracketedArgumentList(SingletonSeparatedList( - Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(1))))))))))))); - } - - private List InvokeSyntax() - { - var statements = new List(); - var arguments = new List(); - - // Generate code for each parameter for the current stage - foreach (IBoundMarshallingGenerator marshaller in _marshallers.NativeParameterMarshallers) - { - // convert arguments for invocation - statements.AddRange(marshaller.Generate(_context)); - arguments.Add(Argument(IdentifierName(marshaller.TypeInfo.InstanceIdentifier))); - } - - if (_marshallers.IsManagedVoidReturn) - { - statements.Add(ExpressionStatement(InvocationExpression(IdentifierName(_signatureContext.MethodName)) - .WithArgumentList(ArgumentList(SeparatedList(arguments))))); - } - else - { - ExpressionSyntax invocation = InvocationExpression(IdentifierName(_signatureContext.MethodName)) - .WithArgumentList(ArgumentList(SeparatedList(arguments))); - - (string _, string nativeIdentifier) = _context.GetIdentifiers(_marshallers.ManagedReturnMarshaller.TypeInfo); - - ExpressionStatementSyntax statement = ExpressionStatement(AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, - IdentifierName(nativeIdentifier), invocation)); - - statements.Add(statement); - } - return statements; - - } - - public (ParameterListSyntax ParameterList, TypeSyntax ReturnType, AttributeListSyntax? ReturnTypeAttributes) GenerateTargetMethodSignatureData() - { - return _marshallers.GenerateTargetMethodSignatureData(_context); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportGenerator.cs index 824bc6b6d6c05e..f5dfa0082aa9e9 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportGenerator.cs @@ -146,7 +146,7 @@ private static MemberDeclarationSyntax PrintGeneratedSource( .WithAttributeLists(SingletonList(AttributeList(SingletonSeparatedList( Attribute(IdentifierName(Constants.DebuggerNonUserCodeAttribute)))))) .WithParameterList(ParameterList(SingletonSeparatedList( - Parameter(Identifier("__arguments_buffer")).WithType(PointerType(ParseTypeName(Constants.JSMarshalerArgumentGlobal)))))) + Parameter(Identifier(Constants.ArgumentsBuffer)).WithType(PointerType(ParseTypeName(Constants.JSMarshalerArgumentGlobal)))))) .WithBody(wrapperStatements); MemberDeclarationSyntax toPrint = containingSyntaxContext.WrapMembersInContainingSyntaxWithUnsafeModifier(wrappperMethod); @@ -225,7 +225,7 @@ private static NamespaceDeclarationSyntax GenerateRegSource( if (methods.IsEmpty) return NamespaceDeclaration(IdentifierName(generatedNamespace)); var registerStatements = new List(); - registerStatements.AddRange(JSExportCodeGenerator.GenerateJSExportArchitectureCheck()); + registerStatements.AddRange(GenerateJSExportArchitectureCheck()); var attributes = new List(); foreach (var m in methods) @@ -302,23 +302,76 @@ private static NamespaceDeclarationSyntax GenerateRegSource( return ns; } + private static StatementSyntax[] GenerateJSExportArchitectureCheck() + { + return [ + IfStatement( + BinaryExpression(SyntaxKind.LogicalOrExpression, + IdentifierName("initialized"), + BinaryExpression(SyntaxKind.NotEqualsExpression, + IdentifierName(Constants.OSArchitectureGlobal), + IdentifierName(Constants.ArchitectureWasmGlobal))), + ReturnStatement()), + ExpressionStatement( + AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + IdentifierName("initialized"), + LiteralExpression(SyntaxKind.TrueLiteralExpression))), + ]; + } + private static (MemberDeclarationSyntax, StatementSyntax, AttributeListSyntax, ImmutableArray) GenerateSource( IncrementalStubGenerationContext incrementalContext) { var diagnostics = new GeneratorDiagnosticsBag(new DescriptorProvider(), incrementalContext.DiagnosticLocation, SR.ResourceManager, typeof(FxResources.Microsoft.Interop.JavaScript.JSImportGenerator.SR)); // Generate stub code - var stubGenerator = new JSExportCodeGenerator( - incrementalContext.SignatureContext.SignatureContext.ElementTypeInformation, - incrementalContext.JSExportData, - incrementalContext.SignatureContext, + ImmutableArray signatureElements = incrementalContext.SignatureContext.SignatureContext.ElementTypeInformation; + + ImmutableArray allElements = signatureElements + .Add(new TypePositionInfo( + new ReferenceTypeInfo(Constants.ExceptionGlobal, Constants.ExceptionGlobal), + new JSMarshallingInfo(NoMarshallingInfo.Instance, new JSSimpleTypeInfo(KnownManagedType.Exception, ParseTypeName(Constants.ExceptionGlobal))) + { + JSType = System.Runtime.InteropServices.JavaScript.JSTypeFlags.Error, + }) + { + InstanceIdentifier = Constants.ArgumentException, + ManagedIndex = TypePositionInfo.ExceptionIndex, + NativeIndex = signatureElements.Length, // Insert at the end of the argument list + RefKind = RefKind.Out, // We'll treat it as a separate out parameter. + }); + + for (int i = 0; i < allElements.Length; i++) + { + if (allElements[i].IsNativeReturnPosition && allElements[i].ManagedType != SpecialTypeInfo.Void) + { + // The runtime may partially initialize the native return value. + // To preserve this information, we must pass the native return value as an out parameter. + allElements = allElements.SetItem(i, allElements[i] with + { + ManagedIndex = TypePositionInfo.ReturnIndex, + NativeIndex = allElements.Length, // Insert at the end of the argument list + RefKind = RefKind.Out, // We'll treat it as a separate out parameter. + }); + } + } + + var stubGenerator = new UnmanagedToManagedStubGenerator( + allElements, diagnostics, - new JSGeneratorResolver()); + new CompositeMarshallingGeneratorResolver( + new NoSpanAndTaskMixingResolver(), + new JSGeneratorResolver())); var wrapperName = "__Wrapper_" + incrementalContext.StubMethodSyntaxTemplate.Identifier + "_" + incrementalContext.SignatureContext.TypesHash; - BlockSyntax wrapper = stubGenerator.GenerateJSExportBody(); - StatementSyntax registration = stubGenerator.GenerateJSExportRegistration(); + const string innerWrapperName = "__Stub"; + + BlockSyntax wrapperToInnerStubBlock = Block( + CreateWrapperToInnerStubCall(signatureElements, innerWrapperName), + GenerateInnerLocalFunction(incrementalContext, innerWrapperName, stubGenerator)); + + StatementSyntax registration = GenerateJSExportRegistration(incrementalContext.SignatureContext); AttributeListSyntax registrationAttribute = AttributeList(SingletonSeparatedList(Attribute(IdentifierName(Constants.DynamicDependencyAttributeGlobal)) .WithArgumentList(AttributeArgumentList(SeparatedList(new[]{ AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(wrapperName))), @@ -327,11 +380,76 @@ private static (MemberDeclarationSyntax, StatementSyntax, AttributeListSyntax, I } ))))); - return (PrintGeneratedSource(incrementalContext.ContainingSyntaxContext, wrapper, wrapperName), + return (PrintGeneratedSource(incrementalContext.ContainingSyntaxContext, wrapperToInnerStubBlock, wrapperName), registration, registrationAttribute, incrementalContext.Diagnostics.Array.AddRange(diagnostics.Diagnostics)); } + private static ExpressionStatementSyntax CreateWrapperToInnerStubCall(ImmutableArray signatureElements, string innerWrapperName) + { + List arguments = []; + bool hasReturn = true; + foreach (var nativeArg in signatureElements.Where(e => e.NativeIndex != TypePositionInfo.UnsetIndex).OrderBy(e => e.NativeIndex)) + { + if (nativeArg.IsNativeReturnPosition) + { + if (nativeArg.ManagedType == SpecialTypeInfo.Void) + { + hasReturn = false; + } + continue; + } + arguments.Add( + Argument( + ElementAccessExpression( + IdentifierName(Constants.ArgumentsBuffer), + BracketedArgumentList(SingletonSeparatedList(Argument( + LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(nativeArg.NativeIndex + 2)))))))); + } + + arguments.Add(Argument(IdentifierName(Constants.ArgumentsBuffer))); + + if (hasReturn) + { + arguments.Add( + Argument( + BinaryExpression( + SyntaxKind.AddExpression, + IdentifierName(Constants.ArgumentsBuffer), + LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(1))))); + } + + return ExpressionStatement( + InvocationExpression(IdentifierName(innerWrapperName)) + .WithArgumentList(ArgumentList(SeparatedList(arguments)))); + } + + private static LocalFunctionStatementSyntax GenerateInnerLocalFunction(IncrementalStubGenerationContext context, string innerFunctionName, UnmanagedToManagedStubGenerator stubGenerator) + { + var (parameters, returnType, _) = stubGenerator.GenerateAbiMethodSignatureData(); + return LocalFunctionStatement( + returnType, + innerFunctionName) + .WithBody(stubGenerator.GenerateStubBody(IdentifierName(context.SignatureContext.MethodName))) + .WithParameterList(parameters) + .WithAttributeLists(SingletonList(AttributeList(SingletonSeparatedList( + Attribute(IdentifierName(Constants.DebuggerNonUserCodeAttribute)))))); + } + + private static ExpressionStatementSyntax GenerateJSExportRegistration(JSSignatureContext context) + { + var signatureArgs = new List + { + Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(context.QualifiedMethodName))), + Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(context.TypesHash))), + SignatureBindingHelpers.CreateSignaturesArgument(context.SignatureContext.ElementTypeInformation, StubCodeContext.DefaultNativeToManagedStub) + }; + + return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(Constants.JSFunctionSignatureGlobal), IdentifierName(Constants.BindCSFunctionMethod))) + .WithArgumentList(ArgumentList(SeparatedList(signatureArgs)))); + } + private static Diagnostic? GetDiagnosticIfInvalidMethodForGeneration(MethodDeclarationSyntax methodSyntax, IMethodSymbol method) { // Verify the method has no generic types or defined implementation diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSGeneratorFactory.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSGeneratorFactory.cs index f5f61c413bc117..51b6d5764b1b76 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSGeneratorFactory.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSGeneratorFactory.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Data; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.Interop.JavaScript { @@ -16,7 +17,7 @@ internal sealed class JSGeneratorResolver : IMarshallingGeneratorResolver public ResolvedGenerator Create(TypePositionInfo info, StubCodeContext context) { Debug.Assert(context != null); - if (info.IsByRef || info.ByValueContentsMarshalKind != ByValueContentsMarshalKind.Default) + if (!info.IsManagedExceptionPosition && !info.IsManagedReturnPosition && (info.IsByRef || info.ByValueContentsMarshalKind != ByValueContentsMarshalKind.Default)) { // out of scope for Net7.0 return ResolvedGenerator.NotSupported(info, context, new(info) @@ -24,298 +25,311 @@ public ResolvedGenerator Create(TypePositionInfo info, StubCodeContext context) NotSupportedDetails = SR.InOutRefNotSupported }); } - JSMarshallingInfo jsMarshalingInfo = info.MarshallingAttributeInfo as JSMarshallingInfo; - ResolvedGenerator fail(string failReason) + if (MarshallerHelpers.GetMarshalDirection(info, context) != MarshalDirection.UnmanagedToManaged && info.ManagedIndex == TypePositionInfo.UnsetIndex) { - return ResolvedGenerator.NotSupported(info, context, new(info) - { - NotSupportedDetails = failReason - }); + // Use the InitOnlyJSGenerator for native-only parameters. + return ResolvedGenerator.Resolved(new ImplicitArgumentGenerator(info, context)); } - bool isToJs = info.ManagedIndex != TypePositionInfo.ReturnIndex ^ context.Direction == MarshalDirection.UnmanagedToManaged; + + return Create(info, info.MarshallingAttributeInfo as JSMarshallingInfo, context).Generator; + } + + public static (MarshalerType BaseType, IEnumerable? SubTypes) GetMarshallerTypeForBinding(TypePositionInfo info, StubCodeContext context) + { + var (_, baseType, subTypes) = Create(info, info.MarshallingAttributeInfo as JSMarshallingInfo, context); + return (baseType, subTypes); + } + + private record struct ResolvedGeneratorAndType(ResolvedGenerator Generator, MarshalerType Type, IEnumerable? SubTypes = null); + + private static ResolvedGeneratorAndType Create(TypePositionInfo info, JSMarshallingInfo jsMarshalingInfo, StubCodeContext context) + { + bool isToJs = MarshallerHelpers.GetMarshalDirection(info, context) == MarshalDirection.ManagedToUnmanaged; switch (jsMarshalingInfo) { // invalid case { TypeInfo: JSInvalidTypeInfo }: - return ResolvedGenerator.NotSupported(info, context, new(info)); - - // void - case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.DiscardNoWait }: - return ResolvedGenerator.Resolved(new VoidGenerator(info, context, MarshalerType.DiscardNoWait)); - case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.Discard }: - case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.Void }: - case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.None }: - case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.Missing }: - return ResolvedGenerator.Resolved(new VoidGenerator(info, context, jsMarshalingInfo.JSType == JSTypeFlags.Void ? MarshalerType.Void : MarshalerType.Discard)); - - // discard no void - case { JSType: JSTypeFlags.Discard }: - return fail(SR.DiscardOnlyVoid); - - // oneway no void - case { JSType: JSTypeFlags.DiscardNoWait }: - return fail(SR.DiscardNoWaitOnlyVoid); + return new(ResolvedGenerator.NotSupported(info, context, new(info)), MarshalerType.None); // primitive case { TypeInfo: JSSimpleTypeInfo simple }: - return Create(info, context, isToJs, simple.KnownType, [], jsMarshalingInfo.JSType, Array.Empty(), fail); + return Create(info, context, isToJs, simple.KnownType, [], jsMarshalingInfo.JSType, []); // nullable case { TypeInfo: JSNullableTypeInfo nullable }: - return Create(info, context, isToJs, nullable.KnownType, [nullable.ResultTypeInfo.KnownType], jsMarshalingInfo.JSType, null, fail); + return Create(info, context, isToJs, nullable.KnownType, [nullable.ResultTypeInfo.KnownType], jsMarshalingInfo.JSType, null); // array case { TypeInfo: JSArrayTypeInfo array }: - return Create(info, context, isToJs, array.KnownType, [array.ElementTypeInfo.KnownType], jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments, fail); + return Create(info, context, isToJs, array.KnownType, [array.ElementTypeInfo.KnownType], jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments); // array segment case { TypeInfo: JSArraySegmentTypeInfo segment }: - return Create(info, context, isToJs, segment.KnownType, [segment.ElementTypeInfo.KnownType], jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments, fail); + return Create(info, context, isToJs, segment.KnownType, [segment.ElementTypeInfo.KnownType], jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments); // span case { TypeInfo: JSSpanTypeInfo span }: - return Create(info, context, isToJs, span.KnownType, [span.ElementTypeInfo.KnownType], jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments, fail); + return Create(info, context, isToJs, span.KnownType, [span.ElementTypeInfo.KnownType], jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments); // task case { TypeInfo: JSTaskTypeInfo(JSSimpleTypeInfo(KnownManagedType.Void)) task }: - return Create(info, context, isToJs, task.KnownType, [], jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments, fail); + return Create(info, context, isToJs, task.KnownType, [], jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments); case { TypeInfo: JSTaskTypeInfo task }: - return Create(info, context, isToJs, task.KnownType, [task.ResultTypeInfo.KnownType], jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments, fail); + return Create(info, context, isToJs, task.KnownType, [task.ResultTypeInfo.KnownType], jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments); // action + function case { TypeInfo: JSFunctionTypeInfo function }: - return Create(info, context, isToJs, function.KnownType, function.ArgsTypeInfo.Select(a => a.KnownType).ToArray(), jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments, fail); + return Create(info, context, isToJs, function.KnownType, function.ArgsTypeInfo.Select(a => a.KnownType).ToArray(), jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments); default: - return ResolvedGenerator.NotSupported(info, context, new(info)); + return new(ResolvedGenerator.NotSupported(info, context, new(info)), MarshalerType.None); } } - internal static ResolvedGenerator Create(TypePositionInfo info, StubCodeContext context, bool isToJs, KnownManagedType marshaledType, KnownManagedType[] argumentTypes, JSTypeFlags jsType, JSTypeFlags[] jsTypeArguments, Func failWithReason) + private static ResolvedGeneratorAndType Create(TypePositionInfo info, StubCodeContext context, bool isToJs, KnownManagedType marshaledType, KnownManagedType[] argumentTypes, JSTypeFlags jsType, JSTypeFlags[] jsTypeArguments) { - switch (marshaledType) + return (marshaledType, jsType, argumentTypes, jsTypeArguments) switch { + // void + (KnownManagedType.Void, JSTypeFlags.Void, _, _) => resolved(new Forwarder().Bind(info, context), MarshalerType.Void), + (KnownManagedType.Void, JSTypeFlags.None or JSTypeFlags.Discard, _, _) => resolved(new Forwarder().Bind(info, context), MarshalerType.Discard), + (KnownManagedType.Void, JSTypeFlags.DiscardNoWait, _, _) => resolved(new Forwarder().Bind(info, context), MarshalerType.DiscardNoWait), + + // void missing + (KnownManagedType.Void, JSTypeFlags.Missing, _, _) => resolved(new Forwarder().Bind(info, context), MarshalerType.Discard), + // primitive - case KnownManagedType.Boolean when jsType == JSTypeFlags.Boolean: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Boolean)); - case KnownManagedType.Byte when jsType == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Byte)); - case KnownManagedType.Char when jsType == JSTypeFlags.String: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Char)); - case KnownManagedType.Int16 when jsType == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int16)); - case KnownManagedType.Int32 when jsType == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int32)); - case KnownManagedType.Int64 when jsType == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int52)); - case KnownManagedType.Int64 when jsType == JSTypeFlags.BigInt: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.BigInt64)); - case KnownManagedType.Single when jsType == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Single)); - case KnownManagedType.Double when jsType == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Double)); - case KnownManagedType.IntPtr when jsType == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.IntPtr)); - case KnownManagedType.DateTime when jsType == JSTypeFlags.Date: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.DateTime)); - case KnownManagedType.DateTimeOffset when jsType == JSTypeFlags.Date: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.DateTimeOffset)); - case KnownManagedType.Exception when jsType == JSTypeFlags.Error: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Exception)); - case KnownManagedType.JSObject when jsType == JSTypeFlags.Object: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.JSObject)); - case KnownManagedType.String when jsType == JSTypeFlags.String: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.String)); - case KnownManagedType.Object when jsType == JSTypeFlags.Any: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Object)); + (KnownManagedType.Boolean, JSTypeFlags.Boolean, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Boolean), MarshalerType.Boolean), + (KnownManagedType.Byte, JSTypeFlags.Number, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Byte), MarshalerType.Byte), + (KnownManagedType.Char, JSTypeFlags.String, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Char), MarshalerType.Char), + (KnownManagedType.Int16, JSTypeFlags.Number, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int16), MarshalerType.Int16), + (KnownManagedType.Int32, JSTypeFlags.Number, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int32), MarshalerType.Int32), + (KnownManagedType.Int64, JSTypeFlags.Number, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int52), MarshalerType.Int52), + (KnownManagedType.Int64, JSTypeFlags.BigInt, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.BigInt64), MarshalerType.BigInt64), + (KnownManagedType.Single, JSTypeFlags.Number, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Single), MarshalerType.Single), + (KnownManagedType.Double, JSTypeFlags.Number, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Double), MarshalerType.Double), + (KnownManagedType.IntPtr, JSTypeFlags.Number, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.IntPtr), MarshalerType.IntPtr), + (KnownManagedType.DateTime, JSTypeFlags.Date, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.DateTime), MarshalerType.DateTime), + (KnownManagedType.DateTimeOffset, JSTypeFlags.Date, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.DateTimeOffset), MarshalerType.DateTimeOffset), + (KnownManagedType.Exception, JSTypeFlags.Error, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Exception), MarshalerType.Exception), + (KnownManagedType.JSObject, JSTypeFlags.Object, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.JSObject), MarshalerType.JSObject), + (KnownManagedType.String, JSTypeFlags.String, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.String), MarshalerType.String), + (KnownManagedType.Object, JSTypeFlags.Any, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Object), MarshalerType.Object), // primitive missing - case KnownManagedType.Boolean when jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Boolean)); - case KnownManagedType.Byte when jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Byte)); - case KnownManagedType.Char when jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Char)); - case KnownManagedType.Int16 when jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int16)); - case KnownManagedType.Int32 when jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int32)); - case KnownManagedType.Single when jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Single)); - case KnownManagedType.Double when jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Double)); - case KnownManagedType.IntPtr when jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.IntPtr)); - case KnownManagedType.Exception when jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Exception)); - case KnownManagedType.JSObject when jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.JSObject)); - case KnownManagedType.String when jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new PrimitiveJSGenerator(info, context, MarshalerType.String)); + (KnownManagedType.Boolean, JSTypeFlags.Missing, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Boolean), MarshalerType.Boolean), + (KnownManagedType.Byte, JSTypeFlags.Missing, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Byte), MarshalerType.Byte), + (KnownManagedType.Char, JSTypeFlags.Missing, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Char), MarshalerType.Char), + (KnownManagedType.Int16, JSTypeFlags.Missing, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int16), MarshalerType.Int16), + (KnownManagedType.Int32, JSTypeFlags.Missing, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int32), MarshalerType.Int32), + (KnownManagedType.Single, JSTypeFlags.Missing, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Single), MarshalerType.Single), + (KnownManagedType.Double, JSTypeFlags.Missing, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Double), MarshalerType.Double), + (KnownManagedType.IntPtr, JSTypeFlags.Missing, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.IntPtr), MarshalerType.IntPtr), + (KnownManagedType.Exception, JSTypeFlags.Missing, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Exception), MarshalerType.Exception), + (KnownManagedType.JSObject, JSTypeFlags.Missing, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.JSObject), MarshalerType.JSObject), + (KnownManagedType.String, JSTypeFlags.Missing, _, _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.String), MarshalerType.String), // primitive forced - case KnownManagedType.Int64 when jsType == JSTypeFlags.Missing: - case KnownManagedType.DateTime when jsType == JSTypeFlags.Missing: - case KnownManagedType.DateTimeOffset when jsType == JSTypeFlags.Missing: - case KnownManagedType.Object when jsType == JSTypeFlags.Missing: - return failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)); + (KnownManagedType.Int64, JSTypeFlags.Missing, _, _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), + (KnownManagedType.DateTime, JSTypeFlags.Missing, _, _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), + (KnownManagedType.DateTimeOffset, JSTypeFlags.Missing, _, _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), + (KnownManagedType.Object, JSTypeFlags.Missing, _, _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), // nullable - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Boolean && jsType == JSTypeFlags.Boolean: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Boolean)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Byte && jsType == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Byte)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Char && jsType == JSTypeFlags.String: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Byte)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Int16 && jsType == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Int16)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Int32 && jsType == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Int32)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Int64 && jsType == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Int52)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Int64 && jsType == JSTypeFlags.BigInt: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.BigInt64)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Double && jsType == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Double)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Single && jsType == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Single)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.IntPtr && jsType == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.IntPtr)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.DateTime && jsType == JSTypeFlags.Date: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.DateTime)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.DateTimeOffset && jsType == JSTypeFlags.Date: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.DateTimeOffset)); + (KnownManagedType.Nullable, JSTypeFlags.Boolean, [KnownManagedType.Boolean], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Boolean), MarshalerType.Nullable, [MarshalerType.Boolean]), + (KnownManagedType.Nullable, JSTypeFlags.Number, [KnownManagedType.Byte], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Byte), MarshalerType.Nullable, [MarshalerType.Byte]), + (KnownManagedType.Nullable, JSTypeFlags.String, [KnownManagedType.Char], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Char), MarshalerType.Nullable, [MarshalerType.Char]), + (KnownManagedType.Nullable, JSTypeFlags.Number, [KnownManagedType.Int16], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int16), MarshalerType.Nullable, [MarshalerType.Int16]), + (KnownManagedType.Nullable, JSTypeFlags.Number, [KnownManagedType.Int32], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int32), MarshalerType.Nullable, [MarshalerType.Int32]), + (KnownManagedType.Nullable, JSTypeFlags.Number, [KnownManagedType.Int64], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int52), MarshalerType.Nullable, [MarshalerType.Int52]), + (KnownManagedType.Nullable, JSTypeFlags.BigInt, [KnownManagedType.Int64], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.BigInt64), MarshalerType.Nullable, [MarshalerType.BigInt64]), + (KnownManagedType.Nullable, JSTypeFlags.Number, [KnownManagedType.Single], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Single), MarshalerType.Nullable, [MarshalerType.Single]), + (KnownManagedType.Nullable, JSTypeFlags.Number, [KnownManagedType.Double], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Double), MarshalerType.Nullable, [MarshalerType.Double]), + (KnownManagedType.Nullable, JSTypeFlags.Number, [KnownManagedType.IntPtr], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.IntPtr), MarshalerType.Nullable, [MarshalerType.IntPtr]), + (KnownManagedType.Nullable, JSTypeFlags.Date, [KnownManagedType.DateTime], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.DateTime), MarshalerType.Nullable, [MarshalerType.DateTime]), + (KnownManagedType.Nullable, JSTypeFlags.Date, [KnownManagedType.DateTimeOffset], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.DateTimeOffset), MarshalerType.Nullable, [MarshalerType.DateTimeOffset]), // nullable missing - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Boolean && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Boolean)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Byte && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Byte)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Char && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Byte)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Int16 && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Int16)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Int32 && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Int32)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Single && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Single)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Double && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.Double)); - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.IntPtr && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new NullableJSGenerator(info, context, MarshalerType.IntPtr)); + (KnownManagedType.Nullable, JSTypeFlags.Missing, [KnownManagedType.Boolean], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Boolean), MarshalerType.Nullable, [MarshalerType.Boolean]), + (KnownManagedType.Nullable, JSTypeFlags.Missing, [KnownManagedType.Byte], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Byte), MarshalerType.Nullable, [MarshalerType.Byte]), + (KnownManagedType.Nullable, JSTypeFlags.Missing, [KnownManagedType.Char], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Char), MarshalerType.Nullable, [MarshalerType.Char]), + (KnownManagedType.Nullable, JSTypeFlags.Missing, [KnownManagedType.Int16], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int16), MarshalerType.Nullable, [MarshalerType.Int16]), + (KnownManagedType.Nullable, JSTypeFlags.Missing, [KnownManagedType.Int32], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Int32), MarshalerType.Nullable, [MarshalerType.Int32]), + (KnownManagedType.Nullable, JSTypeFlags.Missing, [KnownManagedType.Single], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Single), MarshalerType.Nullable, [MarshalerType.Single]), + (KnownManagedType.Nullable, JSTypeFlags.Missing, [KnownManagedType.Double], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Double), MarshalerType.Nullable, [MarshalerType.Double]), + (KnownManagedType.Nullable, JSTypeFlags.Missing, [KnownManagedType.IntPtr], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.IntPtr), MarshalerType.Nullable, [MarshalerType.IntPtr]), // nullable forced - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.Int64 && jsType == JSTypeFlags.Missing: - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.DateTime && jsType == JSTypeFlags.Missing: - case KnownManagedType.Nullable when argumentTypes[0] == KnownManagedType.DateTimeOffset && jsType == JSTypeFlags.Missing: - return failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)); + (KnownManagedType.Nullable, JSTypeFlags.Missing, [KnownManagedType.Int64], _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), + (KnownManagedType.Nullable, JSTypeFlags.Missing, [KnownManagedType.DateTime], _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), + (KnownManagedType.Nullable, JSTypeFlags.Missing, [KnownManagedType.DateTimeOffset], _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), + + (KnownManagedType.Nullable, _, _, _) => failWithReason(SR.Format(SR.TypeNotSupportedName, info.ManagedType.FullTypeName)), - case KnownManagedType.Nullable: - return failWithReason(SR.Format(SR.TypeNotSupportedName, info.ManagedType.FullTypeName)); // task - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes.Length == 0 && jsTypeArguments[0] == JSTypeFlags.Void: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Void)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Byte && jsTypeArguments[0] == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Byte)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Boolean && jsTypeArguments[0] == JSTypeFlags.Boolean: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Boolean)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Char && jsTypeArguments[0] == JSTypeFlags.String: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Char)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Int16 && jsTypeArguments[0] == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Int16)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Int32 && jsTypeArguments[0] == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Int32)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Int64 && jsTypeArguments[0] == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Int52)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Int64 && jsTypeArguments[0] == JSTypeFlags.BigInt: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.BigInt64)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.IntPtr && jsTypeArguments[0] == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.IntPtr)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Double && jsTypeArguments[0] == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Double)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Single && jsTypeArguments[0] == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Single)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.JSObject && jsTypeArguments[0] == JSTypeFlags.Object: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.JSObject)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.String && jsTypeArguments[0] == JSTypeFlags.String: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.String)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Exception && jsTypeArguments[0] == JSTypeFlags.Error: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Exception)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.DateTime && jsTypeArguments[0] == JSTypeFlags.Date: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.DateTime)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.DateTimeOffset && jsTypeArguments[0] == JSTypeFlags.Date: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.DateTimeOffset)); - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Object && jsTypeArguments[0] == JSTypeFlags.Any: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Object)); + (KnownManagedType.Task, JSTypeFlags.Promise, [], [JSTypeFlags.Void]) => resolved(new TaskJSGenerator(info, context, MarshalerType.Void), MarshalerType.Task, []), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.Boolean], [JSTypeFlags.Boolean]) => resolved(new TaskJSGenerator(info, context, MarshalerType.Boolean), MarshalerType.Task, [MarshalerType.Boolean]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.Byte], [JSTypeFlags.Number]) => resolved(new TaskJSGenerator(info, context, MarshalerType.Byte), MarshalerType.Task, [MarshalerType.Byte]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.Char], [JSTypeFlags.String]) => resolved(new TaskJSGenerator(info, context, MarshalerType.Char), MarshalerType.Task, [MarshalerType.Char]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.Int16], [JSTypeFlags.Number]) => resolved(new TaskJSGenerator(info, context, MarshalerType.Int16), MarshalerType.Task, [MarshalerType.Int16]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.Int32], [JSTypeFlags.Number]) => resolved(new TaskJSGenerator(info, context, MarshalerType.Int32), MarshalerType.Task, [MarshalerType.Int32]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.Int64], [JSTypeFlags.Number]) => resolved(new TaskJSGenerator(info, context, MarshalerType.Int52), MarshalerType.Task, [MarshalerType.Int52]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.Int64], [JSTypeFlags.BigInt]) => resolved(new TaskJSGenerator(info, context, MarshalerType.BigInt64), MarshalerType.Task, [MarshalerType.BigInt64]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.Single], [JSTypeFlags.Number]) => resolved(new TaskJSGenerator(info, context, MarshalerType.Single), MarshalerType.Task, [MarshalerType.Single]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.Double], [JSTypeFlags.Number]) => resolved(new TaskJSGenerator(info, context, MarshalerType.Double), MarshalerType.Task, [MarshalerType.Double]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.IntPtr], [JSTypeFlags.Number]) => resolved(new TaskJSGenerator(info, context, MarshalerType.IntPtr), MarshalerType.Task, [MarshalerType.IntPtr]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.DateTime], [JSTypeFlags.Date]) => resolved(new TaskJSGenerator(info, context, MarshalerType.DateTime), MarshalerType.Task, [MarshalerType.DateTime]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.DateTimeOffset], [JSTypeFlags.Date]) => resolved(new TaskJSGenerator(info, context, MarshalerType.DateTimeOffset), MarshalerType.Task, [MarshalerType.DateTimeOffset]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.Exception], [JSTypeFlags.Error]) => resolved(new TaskJSGenerator(info, context, MarshalerType.Exception), MarshalerType.Task, [MarshalerType.Exception]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.JSObject], [JSTypeFlags.Object]) => resolved(new TaskJSGenerator(info, context, MarshalerType.JSObject), MarshalerType.Task, [MarshalerType.JSObject]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.String], [JSTypeFlags.String]) => resolved(new TaskJSGenerator(info, context, MarshalerType.String), MarshalerType.Task, [MarshalerType.String]), + (KnownManagedType.Task, JSTypeFlags.Promise, [KnownManagedType.Object], [JSTypeFlags.Any]) => resolved(new TaskJSGenerator(info, context, MarshalerType.Object), MarshalerType.Task, [MarshalerType.Object]), // task missing - case KnownManagedType.Task when jsType == JSTypeFlags.Missing && argumentTypes.Length == 0: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Void)); - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.Boolean && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Boolean)); - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.Byte && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Byte)); - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.Char && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Char)); - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.Int16 && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Int16)); - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.Int32 && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Int32)); - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.Single && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Single)); - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.Double && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Double)); - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.IntPtr && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.IntPtr)); - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.JSObject && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.JSObject)); - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.String && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.String)); - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.Exception && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new TaskJSGenerator(info, context, MarshalerType.Exception)); + (KnownManagedType.Task, JSTypeFlags.Missing, [], _) => resolved(new TaskJSGenerator(info, context, MarshalerType.Void), MarshalerType.Task, []), + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.Boolean], _) => resolved(new TaskJSGenerator(info, context, MarshalerType.Boolean), MarshalerType.Task, [MarshalerType.Boolean]), + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.Byte], _) => resolved(new TaskJSGenerator(info, context, MarshalerType.Byte), MarshalerType.Task, [MarshalerType.Byte]), + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.Char], _) => resolved(new TaskJSGenerator(info, context, MarshalerType.Char), MarshalerType.Task, [MarshalerType.Char]), + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.Int16], _) => resolved(new TaskJSGenerator(info, context, MarshalerType.Int16), MarshalerType.Task, [MarshalerType.Int16]), + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.Int32], _) => resolved(new TaskJSGenerator(info, context, MarshalerType.Int32), MarshalerType.Task, [MarshalerType.Int32]), + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.Single], _) => resolved(new TaskJSGenerator(info, context, MarshalerType.Single), MarshalerType.Task, [MarshalerType.Single]), + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.Double], _) => resolved(new TaskJSGenerator(info, context, MarshalerType.Double), MarshalerType.Task, [MarshalerType.Double]), + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.IntPtr], _) => resolved(new TaskJSGenerator(info, context, MarshalerType.IntPtr), MarshalerType.Task, [MarshalerType.IntPtr]), + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.Exception], _) => resolved(new TaskJSGenerator(info, context, MarshalerType.Exception), MarshalerType.Task, [MarshalerType.Exception]), + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.JSObject], _) => resolved(new TaskJSGenerator(info, context, MarshalerType.JSObject), MarshalerType.Task, [MarshalerType.JSObject]), + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.String], _) => resolved(new TaskJSGenerator(info, context, MarshalerType.String), MarshalerType.Task, [MarshalerType.String]), // task forced - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.Int64 && jsType == JSTypeFlags.Missing: - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.DateTime && jsType == JSTypeFlags.Missing: - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.DateTimeOffset && jsType == JSTypeFlags.Missing: - case KnownManagedType.Task when argumentTypes[0] == KnownManagedType.Object && jsType == JSTypeFlags.Missing: - return failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)); + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.Int64], _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.DateTime], _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.DateTimeOffset], _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), + (KnownManagedType.Task, JSTypeFlags.Missing, [KnownManagedType.Object], _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), - case KnownManagedType.Task when jsType == JSTypeFlags.Promise && jsTypeArguments.Length == 1: - return failWithReason(SR.Format(SR.TypeNotSupportedName, info.ManagedType.FullTypeName)); + (KnownManagedType.Task, JSTypeFlags.Promise, _, [_]) => failWithReason(SR.Format(SR.TypeNotSupportedName, info.ManagedType.FullTypeName)), // array - case KnownManagedType.Array when jsType == JSTypeFlags.Array && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Byte && jsTypeArguments[0] == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new ArrayJSGenerator(info, context, MarshalerType.Byte)); - case KnownManagedType.Array when jsType == JSTypeFlags.Array && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.String && jsTypeArguments[0] == JSTypeFlags.String: return ResolvedGenerator.Resolved(new ArrayJSGenerator(info, context, MarshalerType.String)); - case KnownManagedType.Array when jsType == JSTypeFlags.Array && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Double && jsTypeArguments[0] == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new ArrayJSGenerator(info, context, MarshalerType.Double)); - case KnownManagedType.Array when jsType == JSTypeFlags.Array && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Int32 && jsTypeArguments[0] == JSTypeFlags.Number: return ResolvedGenerator.Resolved(new ArrayJSGenerator(info, context, MarshalerType.Int32)); - case KnownManagedType.Array when jsType == JSTypeFlags.Array && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.JSObject && jsTypeArguments[0] == JSTypeFlags.Object: return ResolvedGenerator.Resolved(new ArrayJSGenerator(info, context, MarshalerType.JSObject)); - case KnownManagedType.Array when jsType == JSTypeFlags.Array && jsTypeArguments.Length == 1 && argumentTypes[0] == KnownManagedType.Object && jsTypeArguments[0] == JSTypeFlags.Any: return ResolvedGenerator.Resolved(new ArrayJSGenerator(info, context, MarshalerType.Object)); + (KnownManagedType.Array, JSTypeFlags.Array, [KnownManagedType.Byte], [JSTypeFlags.Number]) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Array), MarshalerType.Array, [MarshalerType.Byte]), + (KnownManagedType.Array, JSTypeFlags.Array, [KnownManagedType.String], [JSTypeFlags.String]) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Array), MarshalerType.Array, [MarshalerType.String]), + (KnownManagedType.Array, JSTypeFlags.Array, [KnownManagedType.Double], [JSTypeFlags.Number]) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Array), MarshalerType.Array, [MarshalerType.Double]), + (KnownManagedType.Array, JSTypeFlags.Array, [KnownManagedType.Int32], [JSTypeFlags.Number]) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Array), MarshalerType.Array, [MarshalerType.Int32]), + (KnownManagedType.Array, JSTypeFlags.Array, [KnownManagedType.JSObject], [JSTypeFlags.Object]) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Array), MarshalerType.Array, [MarshalerType.JSObject]), + (KnownManagedType.Array, JSTypeFlags.Array, [KnownManagedType.Object], [JSTypeFlags.Any]) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Array), MarshalerType.Array, [MarshalerType.Object]), // array missing - case KnownManagedType.Array when argumentTypes[0] == KnownManagedType.Byte && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new ArrayJSGenerator(info, context, MarshalerType.Byte)); - case KnownManagedType.Array when argumentTypes[0] == KnownManagedType.String && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new ArrayJSGenerator(info, context, MarshalerType.String)); - case KnownManagedType.Array when argumentTypes[0] == KnownManagedType.Double && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new ArrayJSGenerator(info, context, MarshalerType.Double)); - case KnownManagedType.Array when argumentTypes[0] == KnownManagedType.Int32 && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new ArrayJSGenerator(info, context, MarshalerType.Int32)); - case KnownManagedType.Array when argumentTypes[0] == KnownManagedType.JSObject && jsType == JSTypeFlags.Missing: return ResolvedGenerator.Resolved(new ArrayJSGenerator(info, context, MarshalerType.JSObject)); - case KnownManagedType.Array when jsType == JSTypeFlags.Array && jsTypeArguments.Length == 1: - return failWithReason(SR.Format(SR.TypeNotSupportedName, info.ManagedType.FullTypeName)); + (KnownManagedType.Array, JSTypeFlags.Missing, [KnownManagedType.Byte], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Array), MarshalerType.Array, [MarshalerType.Byte]), + (KnownManagedType.Array, JSTypeFlags.Missing, [KnownManagedType.String], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Array), MarshalerType.Array, [MarshalerType.String]), + (KnownManagedType.Array, JSTypeFlags.Missing, [KnownManagedType.Double], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Array), MarshalerType.Array, [MarshalerType.Double]), + (KnownManagedType.Array, JSTypeFlags.Missing, [KnownManagedType.Int32], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Array), MarshalerType.Array, [MarshalerType.Int32]), + (KnownManagedType.Array, JSTypeFlags.Missing, [KnownManagedType.JSObject], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Array), MarshalerType.Array, [MarshalerType.JSObject]), + + (KnownManagedType.Array, JSTypeFlags.Array, _, [_]) => failWithReason(SR.Format(SR.TypeNotSupportedName, info.ManagedType.FullTypeName)), // array forced - case KnownManagedType.Array when argumentTypes[0] == KnownManagedType.Object && jsType == JSTypeFlags.Missing: - return failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)); + (KnownManagedType.Array, JSTypeFlags.Missing, [KnownManagedType.Object], _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), // span view - case KnownManagedType.Span when jsType == JSTypeFlags.MemoryView && jsTypeArguments.Length != 0: - return failWithReason(null); - case KnownManagedType.Span when jsType == JSTypeFlags.MemoryView && argumentTypes[0] == KnownManagedType.Byte: return ResolvedGenerator.Resolved(new SpanJSGenerator(info, context, MarshalerType.Byte)); - case KnownManagedType.Span when jsType == JSTypeFlags.MemoryView && argumentTypes[0] == KnownManagedType.Int32: return ResolvedGenerator.Resolved(new SpanJSGenerator(info, context, MarshalerType.Int32)); - case KnownManagedType.Span when jsType == JSTypeFlags.MemoryView && argumentTypes[0] == KnownManagedType.Double: return ResolvedGenerator.Resolved(new SpanJSGenerator(info, context, MarshalerType.Double)); + (KnownManagedType.Span, JSTypeFlags.MemoryView, _, [_]) => failWithReason(null!), + (KnownManagedType.Span, JSTypeFlags.MemoryView, [KnownManagedType.Byte], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Span), MarshalerType.Span, [MarshalerType.Byte]), + (KnownManagedType.Span, JSTypeFlags.MemoryView, [KnownManagedType.Int32], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Span), MarshalerType.Span, [MarshalerType.Int32]), + (KnownManagedType.Span, JSTypeFlags.MemoryView, [KnownManagedType.Double], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.Span), MarshalerType.Span, [MarshalerType.Double]), - case KnownManagedType.Span when jsType == JSTypeFlags.MemoryView: - return failWithReason(SR.Format(SR.TypeNotSupportedName, info.ManagedType.FullTypeName)); + (KnownManagedType.Span, JSTypeFlags.MemoryView, _, _) => failWithReason(SR.Format(SR.TypeNotSupportedName, info.ManagedType.FullTypeName)), // span forced - case KnownManagedType.Span when argumentTypes[0] == KnownManagedType.Byte && jsType == JSTypeFlags.Missing: - case KnownManagedType.Span when argumentTypes[0] == KnownManagedType.Int32 && jsType == JSTypeFlags.Missing: - case KnownManagedType.Span when argumentTypes[0] == KnownManagedType.Double && jsType == JSTypeFlags.Missing: - return failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)); + (KnownManagedType.Span, JSTypeFlags.Missing, [KnownManagedType.Byte], _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), + (KnownManagedType.Span, JSTypeFlags.Missing, [KnownManagedType.Int32], _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), + (KnownManagedType.Span, JSTypeFlags.Missing, [KnownManagedType.Double], _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), // segment view - case KnownManagedType.ArraySegment when jsType == JSTypeFlags.MemoryView && jsTypeArguments.Length != 0: - return failWithReason(null); - case KnownManagedType.ArraySegment when jsType == JSTypeFlags.MemoryView && argumentTypes[0] == KnownManagedType.Byte: return ResolvedGenerator.Resolved(new ArraySegmentJSGenerator(info, context, MarshalerType.Byte)); - case KnownManagedType.ArraySegment when jsType == JSTypeFlags.MemoryView && argumentTypes[0] == KnownManagedType.Int32: return ResolvedGenerator.Resolved(new ArraySegmentJSGenerator(info, context, MarshalerType.Int32)); - case KnownManagedType.ArraySegment when jsType == JSTypeFlags.MemoryView && argumentTypes[0] == KnownManagedType.Double: return ResolvedGenerator.Resolved(new ArraySegmentJSGenerator(info, context, MarshalerType.Double)); - case KnownManagedType.ArraySegment when jsType == JSTypeFlags.MemoryView: - return failWithReason(SR.Format(SR.TypeNotSupportedName, info.ManagedType.FullTypeName)); + (KnownManagedType.ArraySegment, JSTypeFlags.MemoryView, _, [_]) => failWithReason(null!), + (KnownManagedType.ArraySegment, JSTypeFlags.MemoryView, [KnownManagedType.Byte], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.ArraySegment), MarshalerType.ArraySegment, [MarshalerType.Byte]), + (KnownManagedType.ArraySegment, JSTypeFlags.MemoryView, [KnownManagedType.Int32], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.ArraySegment), MarshalerType.ArraySegment, [MarshalerType.Int32]), + (KnownManagedType.ArraySegment, JSTypeFlags.MemoryView, [KnownManagedType.Double], _) => resolved(new PrimitiveJSGenerator(info, context, MarshalerType.ArraySegment), MarshalerType.ArraySegment, [MarshalerType.Double]), + + (KnownManagedType.ArraySegment, JSTypeFlags.MemoryView, _, _) => failWithReason(SR.Format(SR.TypeNotSupportedName, info.ManagedType.FullTypeName)), // segment forced - case KnownManagedType.ArraySegment when argumentTypes[0] == KnownManagedType.Byte && jsType == JSTypeFlags.Missing: - case KnownManagedType.ArraySegment when argumentTypes[0] == KnownManagedType.Int32 && jsType == JSTypeFlags.Missing: - case KnownManagedType.ArraySegment when argumentTypes[0] == KnownManagedType.Double && jsType == JSTypeFlags.Missing: - return failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)); + (KnownManagedType.ArraySegment, JSTypeFlags.Missing, [KnownManagedType.Byte], _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), + (KnownManagedType.ArraySegment, JSTypeFlags.Missing, [KnownManagedType.Int32], _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), + (KnownManagedType.ArraySegment, JSTypeFlags.Missing, [KnownManagedType.Double], _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), // function + action - case KnownManagedType.Function when jsType == JSTypeFlags.Function && jsTypeArguments.Length == argumentTypes.Length: - case KnownManagedType.Action when jsType == JSTypeFlags.Function && jsTypeArguments.Length == argumentTypes.Length: - var argsMarshalers = new List(); - for (int i = 0; i < argumentTypes.Length; i++) - { - var isReturn = marshaledType == KnownManagedType.Function && i == jsTypeArguments.Length - 1; - if (argumentTypes[i] == KnownManagedType.Array - || argumentTypes[i] == KnownManagedType.Span - || argumentTypes[i] == KnownManagedType.ArraySegment - || argumentTypes[i] == KnownManagedType.Task - || argumentTypes[i] == KnownManagedType.Function - || argumentTypes[i] == KnownManagedType.Action - || argumentTypes[i] == KnownManagedType.Unknown - ) - { - return failWithReason(SR.Format(SR.FuncArgumentNotSupported, argumentTypes[i])); - } - var gen = Create(info, context, isToJs ^ (!isReturn), argumentTypes[i], Array.Empty(), jsTypeArguments[i], Array.Empty(), failWithReason); - argsMarshalers.Add(((BaseJSGenerator)gen.Generator).Type); - } - var maxArgs = marshaledType == KnownManagedType.Action ? 3 : 4; - var argsMarshallerTypes = argsMarshalers.ToArray(); - if (argsMarshallerTypes.Length > maxArgs) - { - return failWithReason(SR.FuncTooManyArgs); - } - return ResolvedGenerator.Resolved(new FuncJSGenerator(info, context, marshaledType == KnownManagedType.Action, argsMarshallerTypes)); - case KnownManagedType.Action when jsType == JSTypeFlags.Function: - case KnownManagedType.Function when jsType == JSTypeFlags.Function: - return failWithReason(SR.FuncWrongArgumentCount); + (KnownManagedType.Function or KnownManagedType.Action, JSTypeFlags.Function, var argTypes, var argJSTypes) when argTypes.Length != argJSTypes.Length + => failWithReason(SR.Format(SR.TypeNotSupportedName, info.ManagedType.FullTypeName)), + + (KnownManagedType.Function or KnownManagedType.Action, JSTypeFlags.Function or JSTypeFlags.Missing, var argTypes, _) when FindFirstInvalidArgType(argTypes) is KnownManagedType invalidArgType + => failWithReason(SR.Format(SR.FuncArgumentNotSupported, invalidArgType)), + + (KnownManagedType.Function or KnownManagedType.Action, JSTypeFlags.Function, var argTypes, var argJSTypes) => ResolveCallback(marshaledType, argTypes, argJSTypes), // function + action forced - case KnownManagedType.Function when jsType == JSTypeFlags.Missing: - case KnownManagedType.Action when jsType == JSTypeFlags.Missing: - for (int i = 0; i < argumentTypes.Length; i++) + (KnownManagedType.Function or KnownManagedType.Action, JSTypeFlags.Missing, _, _) => failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)), + + // void only JSType on non-void + (not KnownManagedType.Void, JSTypeFlags.Discard, _, _) => failWithReason(SR.DiscardOnlyVoid), + (not KnownManagedType.Void, JSTypeFlags.DiscardNoWait, _, _) => failWithReason(SR.DiscardNoWaitOnlyVoid), + + + _ => failWithReason(SR.Format(SR.TypeNotSupportedName, info.ManagedType.FullTypeName)), + }; + + KnownManagedType? FindFirstInvalidArgType(KnownManagedType[] argumentTypes) + { + foreach (KnownManagedType type in argumentTypes) + { + if (type is KnownManagedType.Array + or KnownManagedType.Span + or KnownManagedType.ArraySegment + or KnownManagedType.Task + or KnownManagedType.Function + or KnownManagedType.Action + or KnownManagedType.Unknown + ) { - if (argumentTypes[i] == KnownManagedType.Array - || argumentTypes[i] == KnownManagedType.Span - || argumentTypes[i] == KnownManagedType.ArraySegment - || argumentTypes[i] == KnownManagedType.Task - || argumentTypes[i] == KnownManagedType.Function - || argumentTypes[i] == KnownManagedType.Action - || argumentTypes[i] == KnownManagedType.Unknown - ) - { - return failWithReason(SR.Format(SR.FuncArgumentNotSupported, argumentTypes[i])); - } + return type; } - return failWithReason(SR.Format(SR.UseJSMarshalAsAttribute, info.ManagedType.FullTypeName)); + } + return null; + } - default: - return failWithReason(SR.Format(SR.TypeNotSupportedName, info.ManagedType.FullTypeName)); + ResolvedGeneratorAndType ResolveCallback(KnownManagedType managedType, KnownManagedType[] argTypes, JSTypeFlags[] argJSTypes) + { + var argsMarshalers = new List(); + for (int i = 0; i < argTypes.Length; i++) + { + var isReturn = managedType == KnownManagedType.Function && i == argJSTypes.Length - 1; + + var gen = Create(info, context, isToJs ^ (!isReturn), argTypes[i], Array.Empty(), argJSTypes[i], Array.Empty()); + argsMarshalers.Add(gen.Type); + } + var maxArgs = managedType == KnownManagedType.Action ? 3 : 4; + MarshalerType[] argsMarshallerTypes = [.. argsMarshalers]; + if (argsMarshallerTypes.Length > maxArgs) + { + return failWithReason(SR.FuncTooManyArgs); + } + return resolved( + new FuncJSGenerator(info, context, managedType == KnownManagedType.Action, argsMarshallerTypes), + managedType == KnownManagedType.Action ? MarshalerType.Action : MarshalerType.Function, + argsMarshallerTypes); + } + + ResolvedGeneratorAndType failWithReason(string failReason) + { + return new( + ResolvedGenerator.NotSupported(info, context, new(info) + { + NotSupportedDetails = failReason + }), + MarshalerType.None); + } + + ResolvedGeneratorAndType resolved(IBoundMarshallingGenerator generator, MarshalerType baseType, IEnumerable? subTypes = null) + { + return new(ResolvedGenerator.Resolved(generator), baseType, subTypes); } } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportCodeGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportCodeGenerator.cs deleted file mode 100644 index 7891bec53f2186..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportCodeGenerator.cs +++ /dev/null @@ -1,212 +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.Linq; -using System.Collections.Generic; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using Microsoft.CodeAnalysis; - -namespace Microsoft.Interop.JavaScript -{ - internal abstract class JSCodeGenerator - { - public const string ReturnIdentifier = "__retVal"; - public const string ReturnNativeIdentifier = $"{ReturnIdentifier}{StubIdentifierContext.GeneratedNativeIdentifierSuffix}"; - public const string InvokeSucceededIdentifier = "__invokeSucceeded"; - } - - internal sealed class JSImportCodeGenerator : JSCodeGenerator - { - private readonly BoundGenerators _marshallers; - - private readonly StubIdentifierContext _context; - private readonly JSImportData _jsImportData; - private readonly JSSignatureContext _signatureContext; - - public JSImportCodeGenerator( - ImmutableArray argTypes, - JSImportData attributeData, - JSSignatureContext signatureContext, - GeneratorDiagnosticsBag diagnosticsBag, - IMarshallingGeneratorResolver generatorResolver) - { - _jsImportData = attributeData; - _signatureContext = signatureContext; - - _marshallers = BoundGenerators.Create(argTypes, generatorResolver, StubCodeContext.DefaultManagedToNativeStub, new EmptyJSGenerator(), out var bindingFailures); - - diagnosticsBag.ReportGeneratorDiagnostics(bindingFailures); - - if (_marshallers.ManagedReturnMarshaller.UsesNativeIdentifier) - { - // If we need a different native return identifier, then recreate the context with the correct identifier before we generate any code. - _context = new DefaultIdentifierContext(ReturnIdentifier, ReturnNativeIdentifier, MarshalDirection.ManagedToUnmanaged) - { - CodeEmitOptions = new(SkipInit: true) - }; - } - else - { - _context = new DefaultIdentifierContext(ReturnIdentifier, ReturnIdentifier, MarshalDirection.ManagedToUnmanaged) - { - CodeEmitOptions = new(SkipInit: true) - }; - } - - // validate task + span mix - if (_marshallers.ManagedReturnMarshaller.TypeInfo.MarshallingAttributeInfo is JSMarshallingInfo(_, JSTaskTypeInfo)) - { - IBoundMarshallingGenerator spanArg = _marshallers.SignatureMarshallers.FirstOrDefault(m => m.TypeInfo.MarshallingAttributeInfo is JSMarshallingInfo(_, JSSpanTypeInfo)); - if (spanArg != default) - { - diagnosticsBag.ReportGeneratorDiagnostic(new GeneratorDiagnostic.NotSupported(spanArg.TypeInfo) - { - NotSupportedDetails = SR.SpanAndTaskNotSupported - }); - } - } - } - - public BlockSyntax GenerateJSImportBody() - { - StatementSyntax invoke = InvokeSyntax(); - GeneratedStatements statements = GeneratedStatements.Create(_marshallers, _context); - bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.CleanupCallerAllocated.IsEmpty || !statements.CleanupCalleeAllocated.IsEmpty; - VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForManagedToUnmanaged(_marshallers, _context, shouldInitializeVariables); - - var setupStatements = new List(); - BindSyntax(setupStatements); - SetupSyntax(setupStatements); - - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) - { - setupStatements.Add(SyntaxFactoryExtensions.Declare(PredefinedType(Token(SyntaxKind.BoolKeyword)), InvokeSucceededIdentifier, initializeToDefault: true)); - } - - setupStatements.AddRange(declarations.Initializations); - setupStatements.AddRange(declarations.Variables); - setupStatements.AddRange(statements.Setup); - - var tryStatements = new List(); - tryStatements.AddRange(statements.Marshal); - tryStatements.AddRange(statements.PinnedMarshal); - - tryStatements.Add(invoke); - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) - { - tryStatements.Add(ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, - IdentifierName(InvokeSucceededIdentifier), - LiteralExpression(SyntaxKind.TrueLiteralExpression)))); - } - - tryStatements.AddRange(statements.NotifyForSuccessfulInvoke); - tryStatements.AddRange(statements.Unmarshal); - - List allStatements = setupStatements; - List finallyStatements = new List(); - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) - { - finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal.Concat(statements.CleanupCalleeAllocated)))); - } - - finallyStatements.AddRange(statements.CleanupCallerAllocated); - if (finallyStatements.Count > 0) - { - // Add try-finally block if there are any statements in the finally block - allStatements.Add( - TryStatement(Block(tryStatements), default, FinallyClause(Block(finallyStatements)))); - } - else - { - allStatements.AddRange(tryStatements); - } - - // Return - if (!_marshallers.IsManagedVoidReturn) - allStatements.Add(ReturnStatement(IdentifierName(_context.GetIdentifiers(_marshallers.ManagedReturnMarshaller.TypeInfo).managed))); - - return Block(allStatements); - } - - private void BindSyntax(List statementsToUpdate) - { - var bindingParameters = - (new ArgumentSyntax[] { - Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(_jsImportData.FunctionName))), - Argument( - _jsImportData.ModuleName == null - ? LiteralExpression(SyntaxKind.NullLiteralExpression) - : LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(_jsImportData.ModuleName))), - CreateSignaturesSyntax(), - }); - - statementsToUpdate.Add(IfStatement(BinaryExpression(SyntaxKind.EqualsExpression, IdentifierName(_signatureContext.BindingName), LiteralExpression(SyntaxKind.NullLiteralExpression)), - Block(SingletonList( - ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, - IdentifierName(_signatureContext.BindingName), - InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(Constants.JSFunctionSignatureGlobal), IdentifierName(Constants.BindJSFunctionMethod))) - .WithArgumentList(ArgumentList(SeparatedList(bindingParameters))))))))); - } - - private ArgumentSyntax CreateSignaturesSyntax() - { - IEnumerable types = _marshallers.ManagedReturnMarshaller is IJSMarshallingGenerator jsGen ? jsGen.GenerateBind() : []; - types = types - .Concat(_marshallers.NativeParameterMarshallers.OfType().SelectMany(p => p.GenerateBind())); - - return Argument(ArrayCreationExpression(ArrayType(IdentifierName(Constants.JSMarshalerTypeGlobal)) - .WithRankSpecifiers(SingletonList(ArrayRankSpecifier(SingletonSeparatedList(OmittedArraySizeExpression()))))) - .WithInitializer(InitializerExpression(SyntaxKind.ArrayInitializerExpression, SeparatedList(types)))); - } - - private void SetupSyntax(List statementsToUpdate) - { - statementsToUpdate.Add(LocalDeclarationStatement( - VariableDeclaration(GenericName(Identifier(Constants.SpanGlobal)).WithTypeArgumentList( - TypeArgumentList(SingletonSeparatedList(IdentifierName(Constants.JSMarshalerArgumentGlobal))))) - .WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier(Constants.ArgumentsBuffer)) - .WithInitializer(EqualsValueClause(StackAllocArrayCreationExpression(ArrayType(IdentifierName(Constants.JSMarshalerArgumentGlobal)) - .WithRankSpecifiers(SingletonList(ArrayRankSpecifier(SingletonSeparatedList( - LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(2 + _marshallers.NativeParameterMarshallers.Length))))))))))))); - - statementsToUpdate.Add(LocalDeclarationStatement(VariableDeclaration(RefType(IdentifierName(Constants.JSMarshalerArgumentGlobal))) - .WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier(Constants.ArgumentException)) - .WithInitializer(EqualsValueClause(RefExpression(ElementAccessExpression(IdentifierName(Constants.ArgumentsBuffer)) - .WithArgumentList(BracketedArgumentList(SingletonSeparatedList( - Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0))))))))))))); - - statementsToUpdate.Add(ExpressionStatement( - InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(Constants.ArgumentException), IdentifierName("Initialize"))))); - - statementsToUpdate.Add(LocalDeclarationStatement(VariableDeclaration(RefType(IdentifierName(Constants.JSMarshalerArgumentGlobal))) - .WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier(Constants.ArgumentReturn)) - .WithInitializer(EqualsValueClause(RefExpression(ElementAccessExpression(IdentifierName(Constants.ArgumentsBuffer)) - .WithArgumentList(BracketedArgumentList(SingletonSeparatedList( - Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(1))))))))))))); - - statementsToUpdate.Add(ExpressionStatement( - InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(Constants.ArgumentReturn), IdentifierName("Initialize"))))); - } - - private ExpressionStatementSyntax InvokeSyntax() - { - return ExpressionStatement(InvocationExpression( - MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(Constants.JSFunctionSignatureGlobal), IdentifierName("InvokeJS"))) - .WithArgumentList(ArgumentList(SeparatedList(new[]{ - Argument(IdentifierName(_signatureContext.BindingName)), - Argument(IdentifierName(Constants.ArgumentsBuffer))})))); - } - - public (ParameterListSyntax ParameterList, TypeSyntax ReturnType, AttributeListSyntax? ReturnTypeAttributes) GenerateTargetMethodSignatureData() - { - return _marshallers.GenerateTargetMethodSignatureData(_context); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportGenerator.cs index c97f6998de9e69..a92ed80b5dd732 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportGenerator.cs @@ -2,14 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices.JavaScript; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using static Microsoft.Interop.SyntaxFactoryExtensions; [assembly: System.Resources.NeutralResourcesLanguage("en-US")] @@ -198,17 +201,162 @@ private static (MemberDeclarationSyntax, ImmutableArray) Generat { var diagnostics = new GeneratorDiagnosticsBag(new DescriptorProvider(), incrementalContext.DiagnosticLocation, SR.ResourceManager, typeof(FxResources.Microsoft.Interop.JavaScript.JSImportGenerator.SR)); + // We need to add the implicit exception and return arguments to the signature and ensure they are initialized before we start to do any marshalling. + const int NumImplicitArguments = 2; + + ImmutableArray originalElementInfo = incrementalContext.SignatureContext.SignatureContext.ElementTypeInformation; + + ImmutableArray.Builder typeInfoBuilder = ImmutableArray.CreateBuilder(originalElementInfo.Length + NumImplicitArguments); + + TypePositionInfo nativeOnlyParameterTemplate = new TypePositionInfo( + SpecialTypeInfo.Void, + new JSMarshallingInfo( + NoMarshallingInfo.Instance, + new JSInvalidTypeInfo())) + { + ManagedIndex = TypePositionInfo.UnsetIndex, + }; + + typeInfoBuilder.Add( + // Add the exception argument + nativeOnlyParameterTemplate with + { + InstanceIdentifier = Constants.ArgumentException, + NativeIndex = 0, + }); + + typeInfoBuilder.Add( + // Add the incoming return argument + nativeOnlyParameterTemplate with + { + InstanceIdentifier = Constants.ArgumentReturn, + NativeIndex = 1, + }); + + bool hasReturn = false; + + foreach (var info in originalElementInfo) + { + TypePositionInfo updatedInfo = info with + { + MarshallingAttributeInfo = info.MarshallingAttributeInfo is JSMarshallingInfo jsInfo + ? jsInfo.AddElementDependencies([typeInfoBuilder[0], typeInfoBuilder[1]]) + : info.MarshallingAttributeInfo, + }; + + if (info.IsManagedReturnPosition) + { + hasReturn = info.ManagedType != SpecialTypeInfo.Void; + } + + if (info.IsNativeReturnPosition) + { + typeInfoBuilder.Add(updatedInfo); + } + else + { + typeInfoBuilder.Add(updatedInfo with + { + NativeIndex = updatedInfo.NativeIndex + NumImplicitArguments + }); + } + } + + ImmutableArray finalElementInfo = typeInfoBuilder.ToImmutable(); + // Generate stub code - var stubGenerator = new JSImportCodeGenerator( - incrementalContext.SignatureContext.SignatureContext.ElementTypeInformation, + var stubGenerator = new ManagedToNativeStubGenerator( + finalElementInfo, + setLastError: false, + diagnostics, + new CompositeMarshallingGeneratorResolver( + new NoSpanAndTaskMixingResolver(), + new JSGeneratorResolver()), + new CodeEmitOptions(SkipInit: true)); + + const string LocalFunctionName = "__InvokeJSFunction"; + + BlockSyntax code = stubGenerator.GenerateStubBody(LocalFunctionName); + + StatementSyntax bindStatement = GenerateBindSyntax( incrementalContext.JSImportData, incrementalContext.SignatureContext, - diagnostics, - new JSGeneratorResolver()); + SignatureBindingHelpers.CreateSignaturesArgument(incrementalContext.SignatureContext.SignatureContext.ElementTypeInformation, StubCodeContext.DefaultManagedToNativeStub)); - BlockSyntax code = stubGenerator.GenerateJSImportBody(); + LocalFunctionStatementSyntax localFunction = GenerateInvokeFunction(LocalFunctionName, incrementalContext.SignatureContext, stubGenerator, hasReturn); + + return (PrintGeneratedSource(incrementalContext.StubMethodSyntaxTemplate, incrementalContext.SignatureContext, incrementalContext.ContainingSyntaxContext, Block(bindStatement, code, localFunction)), incrementalContext.Diagnostics.Array.AddRange(diagnostics.Diagnostics)); + } + + private static IfStatementSyntax GenerateBindSyntax(JSImportData jsImportData, JSSignatureContext signatureContext, ArgumentSyntax signaturesArgument) + { + var bindingParameters = + (new ArgumentSyntax[] { + Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(jsImportData.FunctionName))), + Argument( + jsImportData.ModuleName == null + ? LiteralExpression(SyntaxKind.NullLiteralExpression) + : LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(jsImportData.ModuleName))), + signaturesArgument, + }); + + return IfStatement(BinaryExpression(SyntaxKind.EqualsExpression, IdentifierName(signatureContext.BindingName), LiteralExpression(SyntaxKind.NullLiteralExpression)), + Block(SingletonList( + ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + IdentifierName(signatureContext.BindingName), + InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(Constants.JSFunctionSignatureGlobal), IdentifierName(Constants.BindJSFunctionMethod))) + .WithArgumentList(ArgumentList(SeparatedList(bindingParameters)))))))); + } - return (PrintGeneratedSource(incrementalContext.StubMethodSyntaxTemplate, incrementalContext.SignatureContext, incrementalContext.ContainingSyntaxContext, code), incrementalContext.Diagnostics.Array.AddRange(diagnostics.Diagnostics)); + private static LocalFunctionStatementSyntax GenerateInvokeFunction(string functionName, JSSignatureContext signatureContext, ManagedToNativeStubGenerator stubGenerator, bool hasReturn) + { + var (parameters, returnType, _) = stubGenerator.GenerateTargetMethodSignatureData(); + TypeSyntax jsMarshalerArgument = ParseTypeName(Constants.JSMarshalerArgumentGlobal); + + CollectionExpressionSyntax argumentsBuffer = CollectionExpression( + SeparatedList( + parameters.Parameters + .Select(p => ExpressionElement(IdentifierName(p.Identifier))))); + + List statements = []; + + if (hasReturn) + { + statements.AddRange([ + Declare( + SpanOf(jsMarshalerArgument), + Constants.ArgumentsBuffer, + argumentsBuffer), + MethodInvocationStatement( + IdentifierName(Constants.JSFunctionSignatureGlobal), + IdentifierName("InvokeJS"), + Argument(IdentifierName(signatureContext.BindingName)), + Argument(IdentifierName(Constants.ArgumentsBuffer))), + ReturnStatement( + ElementAccessExpression( + IdentifierName(Constants.ArgumentsBuffer), + BracketedArgumentList(SingletonSeparatedList(Argument( + LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(1))))))) + ]); + } + else + { + statements.Add( + MethodInvocationStatement( + IdentifierName(Constants.JSFunctionSignatureGlobal), + IdentifierName("InvokeJS"), + Argument(IdentifierName(signatureContext.BindingName)), + Argument(argumentsBuffer))); + } + + return LocalFunctionStatement( + hasReturn ? jsMarshalerArgument : PredefinedType(Token(SyntaxKind.VoidKeyword)), + functionName) + .WithBody(Block(statements)) + .WithParameterList(parameters) + .WithAttributeLists(SingletonList(AttributeList(SingletonSeparatedList( + Attribute(IdentifierName(Constants.DebuggerNonUserCodeAttribute)))))); } private static Diagnostic? GetDiagnosticIfInvalidMethodForGeneration(MethodDeclarationSyntax methodSyntax, IMethodSymbol method) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportGenerator.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportGenerator.csproj index d973b24132c6a8..0af96384ebbe8c 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportGenerator.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportGenerator.csproj @@ -27,6 +27,7 @@ + diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSMarshallingInfo.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSMarshallingInfo.cs index edeb7961aadb1e..8d0ed8384486d9 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSMarshallingInfo.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSMarshallingInfo.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.CodeAnalysis; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Runtime.InteropServices.JavaScript; namespace Microsoft.Interop.JavaScript @@ -16,6 +18,14 @@ protected JSMarshallingInfo() public JSTypeFlags JSType { get; init; } public JSTypeFlags[] JSTypeArguments { get; init; } + + private ImmutableArray _elementDependencies = ImmutableArray.Empty; + public override IEnumerable ElementDependencies => _elementDependencies; + + public JSMarshallingInfo AddElementDependencies(IEnumerable elementDependencies) + { + return this with { _elementDependencies = _elementDependencies.AddRange(elementDependencies) }; + } } internal sealed record JSMissingMarshallingInfo : JSMarshallingInfo diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/ArrayJSGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/ArrayJSGenerator.cs deleted file mode 100644 index 5d7d55c644bd7f..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/ArrayJSGenerator.cs +++ /dev/null @@ -1,28 +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.Collections.Generic; -using System.Runtime.InteropServices.JavaScript; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; - -namespace Microsoft.Interop.JavaScript -{ - internal sealed class ArrayJSGenerator : PrimitiveJSGenerator - { - private readonly MarshalerType _elementMarshalerType; - - public ArrayJSGenerator(TypePositionInfo info, StubCodeContext context, MarshalerType elementMarshalerType) - : base(info, context, MarshalerType.Array) - { - _elementMarshalerType = elementMarshalerType; - } - - public override IEnumerable GenerateBind() - { - yield return InvocationExpression(MarshalerTypeName(Type), - ArgumentList(SingletonSeparatedList(Argument(MarshalerTypeName(_elementMarshalerType))))); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/ArraySegmentJSGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/ArraySegmentJSGenerator.cs deleted file mode 100644 index f3785d8bf7e121..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/ArraySegmentJSGenerator.cs +++ /dev/null @@ -1,27 +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.Collections.Generic; -using System.Runtime.InteropServices.JavaScript; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; - -namespace Microsoft.Interop.JavaScript -{ - internal sealed class ArraySegmentJSGenerator : PrimitiveJSGenerator - { - private readonly MarshalerType _elementMarshalerType; - - public ArraySegmentJSGenerator(TypePositionInfo info, StubCodeContext context, MarshalerType elementMarshalerType) - : base(info, context, MarshalerType.ArraySegment) - { - _elementMarshalerType = elementMarshalerType; - } - - public override IEnumerable GenerateBind() - { - yield return InvocationExpression(MarshalerTypeName(Type), - ArgumentList(SingletonSeparatedList(Argument(MarshalerTypeName(_elementMarshalerType))))); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/BaseJSGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/BaseJSGenerator.cs index 1f6eef2346545d..ab1cb7c8de5371 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/BaseJSGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/BaseJSGenerator.cs @@ -7,65 +7,50 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using static Microsoft.Interop.SyntaxFactoryExtensions; + namespace Microsoft.Interop.JavaScript { - internal abstract class BaseJSGenerator : IJSMarshallingGenerator + internal abstract class BaseJSGenerator(TypePositionInfo info, StubCodeContext codeContext) : IBoundMarshallingGenerator { - protected IBoundMarshallingGenerator _inner; - public MarshalerType Type; - - protected BaseJSGenerator(MarshalerType marshalerType, IBoundMarshallingGenerator inner) - { - _inner = inner; - Type = marshalerType; - } + private static ValueTypeInfo JSMarshalerArgument = new ValueTypeInfo(Constants.JSMarshalerArgumentGlobal, Constants.JSMarshalerArgument, IsByRefLike: false); - public TypePositionInfo TypeInfo => _inner.TypeInfo; + public TypePositionInfo TypeInfo => info; - public StubCodeContext CodeContext => _inner.CodeContext; + public StubCodeContext CodeContext => codeContext; - public ManagedTypeInfo NativeType => _inner.NativeType; + public ManagedTypeInfo NativeType => JSMarshalerArgument; - public SignatureBehavior NativeSignatureBehavior => _inner.NativeSignatureBehavior; + public SignatureBehavior NativeSignatureBehavior => TypeInfo.IsByRef ? SignatureBehavior.PointerToNativeType : SignatureBehavior.NativeType; - public ValueBoundaryBehavior ValueBoundaryBehavior => _inner.ValueBoundaryBehavior; + public ValueBoundaryBehavior ValueBoundaryBehavior => TypeInfo.IsByRef ? ValueBoundaryBehavior.AddressOfNativeIdentifier : ValueBoundaryBehavior.NativeIdentifier; - public virtual bool UsesNativeIdentifier => _inner.UsesNativeIdentifier; + public virtual bool UsesNativeIdentifier => true; public ByValueMarshalKindSupport SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, out GeneratorDiagnostic? diagnostic) - => _inner.SupportsByValueMarshalKind(marshalKind, out diagnostic); - - public virtual IEnumerable GenerateBind() { - yield return MarshalerTypeName(Type); + diagnostic = null; + return ByValueMarshalKindSupport.NotSupported; } public virtual IEnumerable Generate(StubIdentifierContext context) { - string argName = context.GetAdditionalIdentifier(TypeInfo, "js_arg"); - - if (context.CurrentStage == StubIdentifierContext.Stage.Setup) + MarshalDirection marshalDirection = MarshallerHelpers.GetMarshalDirection(TypeInfo, CodeContext); + if (context.CurrentStage == StubIdentifierContext.Stage.Setup + && marshalDirection == MarshalDirection.ManagedToUnmanaged + && !TypeInfo.IsManagedReturnPosition) { - if (!TypeInfo.IsManagedReturnPosition) - { - yield return LocalDeclarationStatement(VariableDeclaration(RefType(IdentifierName(Constants.JSMarshalerArgumentGlobal))) - .WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier(argName)) - .WithInitializer(EqualsValueClause(RefExpression(ElementAccessExpression(IdentifierName(Constants.ArgumentsBuffer)) - .WithArgumentList(BracketedArgumentList(SingletonSeparatedList( - Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(TypeInfo.ManagedIndex + 2)))))))))))); - } + var (_, js) = context.GetIdentifiers(TypeInfo); + return [ + ExpressionStatement( + MethodInvocation(TypeSyntaxes.System_Runtime_CompilerServices_Unsafe, IdentifierName("SkipInit"), + Argument(IdentifierName(js)) + .WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)))) + ]; } - foreach (var x in _inner.Generate(context)) - { - yield return x; - } - } - - protected static IdentifierNameSyntax MarshalerTypeName(MarshalerType marshalerType) - { - return IdentifierName(Constants.JSMarshalerTypeGlobalDot + marshalerType.ToString()); + return []; } protected static IdentifierNameSyntax GetToManagedMethod(MarshalerType marshalerType) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/EmptyJSGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/EmptyJSGenerator.cs deleted file mode 100644 index e9704fbdc04d2b..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/EmptyJSGenerator.cs +++ /dev/null @@ -1,20 +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.Collections.Generic; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Microsoft.Interop.JavaScript -{ - internal sealed class EmptyJSGenerator : IUnboundMarshallingGenerator - { - public ManagedTypeInfo AsNativeType(TypePositionInfo info) => info.ManagedType; - public IEnumerable Generate(TypePositionInfo info, StubCodeContext codeContext, StubIdentifierContext identifierContext) => Array.Empty(); - public SignatureBehavior GetNativeSignatureBehavior(TypePositionInfo info) => SignatureBehavior.ManagedTypeAndAttributes; - public ValueBoundaryBehavior GetValueBoundaryBehavior(TypePositionInfo info, StubCodeContext context) => ValueBoundaryBehavior.ManagedIdentifier; - public ByValueMarshalKindSupport SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, TypePositionInfo info, out GeneratorDiagnostic? diagnostic) - => ByValueMarshalKindSupportDescriptor.Default.GetSupport(marshalKind, info, out diagnostic); - public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => false; - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/FuncJSGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/FuncJSGenerator.cs index d4d7f785c55367..f92bc3a4b7691f 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/FuncJSGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/FuncJSGenerator.cs @@ -11,34 +11,16 @@ namespace Microsoft.Interop.JavaScript { - internal sealed class FuncJSGenerator : BaseJSGenerator + internal sealed class FuncJSGenerator(TypePositionInfo info, StubCodeContext context, bool isAction, MarshalerType[] argumentMarshalerTypes) : BaseJSGenerator(info, context) { - private readonly bool _isAction; - private readonly MarshalerType[] _argumentMarshalerTypes; - - public FuncJSGenerator(TypePositionInfo info, StubCodeContext context, bool isAction, MarshalerType[] argumentMarshalerTypes) - : base(isAction ? MarshalerType.Action : MarshalerType.Function, new Forwarder().Bind(info, context)) - { - _isAction = isAction; - _argumentMarshalerTypes = argumentMarshalerTypes; - } - - public override IEnumerable GenerateBind() - { - var args = _argumentMarshalerTypes.Select(x => Argument(MarshalerTypeName(x))).ToList(); - yield return InvocationExpression(MarshalerTypeName(Type), ArgumentList(SeparatedList(args))); - } - public override IEnumerable Generate(StubIdentifierContext context) { - string argName = context.GetAdditionalIdentifier(TypeInfo, "js_arg"); - var target = TypeInfo.IsManagedReturnPosition - ? Constants.ArgumentReturn - : argName; + foreach (var statement in base.Generate(context)) + { + yield return statement; + } - var source = TypeInfo.IsManagedReturnPosition - ? Argument(IdentifierName(context.GetIdentifiers(TypeInfo).native)) - : _inner.AsArgument(context); + var (managed, js) = context.GetIdentifiers(TypeInfo); var jsty = (JSFunctionTypeInfo)((JSMarshallingInfo)TypeInfo.MarshallingAttributeInfo).TypeInfo; var sourceTypes = jsty.ArgsTypeInfo @@ -47,27 +29,22 @@ public override IEnumerable Generate(StubIdentifierContext cont if (context.CurrentStage == StubIdentifierContext.Stage.UnmarshalCapture && CodeContext.Direction == MarshalDirection.ManagedToUnmanaged && TypeInfo.IsManagedReturnPosition) { - yield return ToManagedMethod(target, source, jsty); + yield return ToManagedMethod(js, Argument(IdentifierName(managed)), jsty); } if (context.CurrentStage == StubIdentifierContext.Stage.Marshal && CodeContext.Direction == MarshalDirection.UnmanagedToManaged && TypeInfo.IsManagedReturnPosition) { - yield return ToJSMethod(target, source, jsty); - } - - foreach (var x in base.Generate(context)) - { - yield return x; + yield return ToJSMethod(js, Argument(IdentifierName(managed)), jsty); } if (context.CurrentStage == StubIdentifierContext.Stage.PinnedMarshal && CodeContext.Direction == MarshalDirection.ManagedToUnmanaged && !TypeInfo.IsManagedReturnPosition) { - yield return ToJSMethod(target, source, jsty); + yield return ToJSMethod(js, Argument(IdentifierName(managed)), jsty); } if (context.CurrentStage == StubIdentifierContext.Stage.Unmarshal && CodeContext.Direction == MarshalDirection.UnmanagedToManaged && !TypeInfo.IsManagedReturnPosition) { - yield return ToManagedMethod(target, source, jsty); + yield return ToManagedMethod(js, Argument(IdentifierName(managed)), jsty); } } @@ -77,18 +54,18 @@ private ExpressionStatementSyntax ToManagedMethod(string target, ArgumentSyntax for (int i = 0; i < info.ArgsTypeInfo.Length; i++) { var sourceType = info.ArgsTypeInfo[i]; - if (!_isAction && i + 1 == info.ArgsTypeInfo.Length) + if (!isAction && i + 1 == info.ArgsTypeInfo.Length) { - arguments.Add(ArgToManaged(i, sourceType.Syntax, _argumentMarshalerTypes[i])); + arguments.Add(ArgToManaged(i, sourceType.Syntax, argumentMarshalerTypes[i])); } else { - arguments.Add(ArgToJS(i, sourceType.Syntax, _argumentMarshalerTypes[i])); + arguments.Add(ArgToJS(i, sourceType.Syntax, argumentMarshalerTypes[i])); } } return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(target), GetToManagedMethod(Type))) + IdentifierName(target), GetToManagedMethod(isAction ? MarshalerType.Action : MarshalerType.Function))) .WithArgumentList(ArgumentList(SeparatedList(arguments)))); } @@ -98,18 +75,18 @@ private ExpressionStatementSyntax ToJSMethod(string target, ArgumentSyntax sourc for (int i = 0; i < info.ArgsTypeInfo.Length; i++) { var sourceType = info.ArgsTypeInfo[i]; - if (!_isAction && i + 1 == info.ArgsTypeInfo.Length) + if (!isAction && i + 1 == info.ArgsTypeInfo.Length) { - arguments.Add(ArgToJS(i, sourceType.Syntax, _argumentMarshalerTypes[i])); + arguments.Add(ArgToJS(i, sourceType.Syntax, argumentMarshalerTypes[i])); } else { - arguments.Add(ArgToManaged(i, sourceType.Syntax, _argumentMarshalerTypes[i])); + arguments.Add(ArgToManaged(i, sourceType.Syntax, argumentMarshalerTypes[i])); } } return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(target), GetToJSMethod(Type))) + IdentifierName(target), GetToJSMethod(isAction ? MarshalerType.Action : MarshalerType.Function))) .WithArgumentList(ArgumentList(SeparatedList(arguments)))); } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/IJSMarshallingGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/IJSMarshallingGenerator.cs deleted file mode 100644 index f60406b21e8695..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/IJSMarshallingGenerator.cs +++ /dev/null @@ -1,13 +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.Collections.Generic; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Microsoft.Interop.JavaScript -{ - internal interface IJSMarshallingGenerator : IBoundMarshallingGenerator - { - IEnumerable GenerateBind(); - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/ImplicitArgumentGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/ImplicitArgumentGenerator.cs new file mode 100644 index 00000000000000..346dfb173f1f8c --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/ImplicitArgumentGenerator.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; +using System.Collections.Generic; +using System.Text; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using static Microsoft.Interop.SyntaxFactoryExtensions; + + +namespace Microsoft.Interop.JavaScript +{ + internal sealed class ImplicitArgumentGenerator(TypePositionInfo info, StubCodeContext codeContext) : BaseJSGenerator(info, codeContext) + { + public override IEnumerable Generate(StubIdentifierContext context) + { + if (context.CurrentStage == StubIdentifierContext.Stage.Setup) + { + var (_, js) = context.GetIdentifiers(TypeInfo); + return [ + ExpressionStatement( + MethodInvocation(TypeSyntaxes.System_Runtime_CompilerServices_Unsafe, IdentifierName("SkipInit"), + Argument(IdentifierName(js)) + .WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)))), + // Unlike the other arguments, we need to initialize the implicit arguments + // as they can set some ambient state necessary for the JSImport logic to function. + ExpressionStatement( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(js), + IdentifierName("Initialize")), + ArgumentList())) + ]; + } + + return []; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/NullableJSGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/NullableJSGenerator.cs deleted file mode 100644 index 191c475f1330fa..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/NullableJSGenerator.cs +++ /dev/null @@ -1,24 +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.Collections.Generic; -using System.Runtime.InteropServices.JavaScript; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; - -namespace Microsoft.Interop.JavaScript -{ - internal sealed class NullableJSGenerator : PrimitiveJSGenerator - { - public NullableJSGenerator(TypePositionInfo info, StubCodeContext context, MarshalerType resultMarshalerType) - : base(info, context, resultMarshalerType) - { - } - - public override IEnumerable GenerateBind() - { - yield return InvocationExpression(MarshalerTypeName(MarshalerType.Nullable), - ArgumentList(SingletonSeparatedList(Argument(MarshalerTypeName(Type))))); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/PrimitiveJSGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/PrimitiveJSGenerator.cs index 5da5c9db179751..ab7830a642ab71 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/PrimitiveJSGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/PrimitiveJSGenerator.cs @@ -9,78 +9,43 @@ namespace Microsoft.Interop.JavaScript { - internal class PrimitiveJSGenerator : BaseJSGenerator + internal sealed class PrimitiveJSGenerator(TypePositionInfo info, StubCodeContext context, MarshalerType elementMarshallerType) : BaseJSGenerator(info, context) { - public PrimitiveJSGenerator(MarshalerType marshalerType, IBoundMarshallingGenerator inner) - : base(marshalerType, inner) - { - } - - public PrimitiveJSGenerator(TypePositionInfo info, StubCodeContext context, MarshalerType marshalerType) - : base(marshalerType, new Forwarder().Bind(info, context)) - { - } - // TODO order parameters in such way that affinity capturing parameters are emitted first public override IEnumerable Generate(StubIdentifierContext context) { - string argName = context.GetAdditionalIdentifier(TypeInfo, "js_arg"); - var target = TypeInfo.IsManagedReturnPosition - ? Constants.ArgumentReturn - : argName; - - var source = TypeInfo.IsManagedReturnPosition - ? Argument(IdentifierName(context.GetIdentifiers(TypeInfo).native)) - : _inner.AsArgument(context); - - if (context.CurrentStage == StubIdentifierContext.Stage.UnmarshalCapture && CodeContext.Direction == MarshalDirection.ManagedToUnmanaged && TypeInfo.IsManagedReturnPosition) + foreach (var statement in base.Generate(context)) { - yield return ToManagedMethod(target, source); + yield return statement; } - if (context.CurrentStage == StubIdentifierContext.Stage.Marshal && CodeContext.Direction == MarshalDirection.UnmanagedToManaged && TypeInfo.IsManagedReturnPosition) - { - yield return ToJSMethod(target, source); - } + var (managed, js) = context.GetIdentifiers(TypeInfo); - foreach (var x in base.Generate(context)) - { - yield return x; - } + MarshalDirection marshalDirection = MarshallerHelpers.GetMarshalDirection(TypeInfo, CodeContext); - if (context.CurrentStage == StubIdentifierContext.Stage.PinnedMarshal && CodeContext.Direction == MarshalDirection.ManagedToUnmanaged && !TypeInfo.IsManagedReturnPosition) + if (context.CurrentStage == StubIdentifierContext.Stage.UnmarshalCapture && marshalDirection is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional) { - yield return ToJSMethod(target, source); + yield return ToManagedMethod(js, Argument(IdentifierName(managed))); } - if (context.CurrentStage == StubIdentifierContext.Stage.Unmarshal && CodeContext.Direction == MarshalDirection.UnmanagedToManaged && !TypeInfo.IsManagedReturnPosition) + if (context.CurrentStage == StubIdentifierContext.Stage.Marshal && marshalDirection is MarshalDirection.ManagedToUnmanaged or MarshalDirection.Bidirectional) { - yield return ToManagedMethod(target, source); + yield return ToJSMethod(js, Argument(IdentifierName(managed))); } } - protected virtual ArgumentSyntax ToManagedMethodRefOrOut(ArgumentSyntax argument) - { - return argument.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)); - } - - protected virtual ArgumentSyntax ToJSMethodRefOrOut(ArgumentSyntax argument) - { - return argument; - } - private ExpressionStatementSyntax ToManagedMethod(string target, ArgumentSyntax source) { return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(target), GetToManagedMethod(Type))) - .WithArgumentList(ArgumentList(SingletonSeparatedList(ToManagedMethodRefOrOut(source))))); + IdentifierName(target), GetToManagedMethod(elementMarshallerType))) + .WithArgumentList(ArgumentList(SingletonSeparatedList(source.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)))))); } private ExpressionStatementSyntax ToJSMethod(string target, ArgumentSyntax source) { return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(target), GetToJSMethod(Type))) - .WithArgumentList(ArgumentList(SingletonSeparatedList(ToJSMethodRefOrOut(source))))); + IdentifierName(target), GetToJSMethod(elementMarshallerType))) + .WithArgumentList(ArgumentList(SingletonSeparatedList(source)))); } } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/SpanJSGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/SpanJSGenerator.cs deleted file mode 100644 index afaedf597b6fe5..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/SpanJSGenerator.cs +++ /dev/null @@ -1,27 +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.Collections.Generic; -using System.Runtime.InteropServices.JavaScript; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; - -namespace Microsoft.Interop.JavaScript -{ - internal sealed class SpanJSGenerator : PrimitiveJSGenerator - { - private readonly MarshalerType _elementMarshalerType; - - public SpanJSGenerator(TypePositionInfo info, StubCodeContext context, MarshalerType elementMarshalerType) - : base(info, context, MarshalerType.Span) - { - _elementMarshalerType = elementMarshalerType; - } - - public override IEnumerable GenerateBind() - { - yield return InvocationExpression(MarshalerTypeName(Type), - ArgumentList(SingletonSeparatedList(Argument(MarshalerTypeName(_elementMarshalerType))))); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/TaskJSGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/TaskJSGenerator.cs index 381f4dac813740..2a05d2a7dff92f 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/TaskJSGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/TaskJSGenerator.cs @@ -9,95 +9,66 @@ namespace Microsoft.Interop.JavaScript { - internal sealed class TaskJSGenerator : BaseJSGenerator + internal sealed class TaskJSGenerator(TypePositionInfo info, StubCodeContext context, MarshalerType resultMarshalerType) : BaseJSGenerator(info, context) { - private readonly MarshalerType _resultMarshalerType; - - public TaskJSGenerator(TypePositionInfo info, StubCodeContext context, MarshalerType resultMarshalerType) - : base(MarshalerType.Task, new Forwarder().Bind(info, context)) - { - _resultMarshalerType = resultMarshalerType; - } - - public override IEnumerable GenerateBind() + public override IEnumerable Generate(StubIdentifierContext context) { - var jsty = (JSTaskTypeInfo)((JSMarshallingInfo)TypeInfo.MarshallingAttributeInfo).TypeInfo; - if (jsty.ResultTypeInfo is JSSimpleTypeInfo(KnownManagedType.Void)) - { - yield return InvocationExpression(MarshalerTypeName(MarshalerType.Task), ArgumentList()); - } - else + foreach (var statement in base.Generate(context)) { - yield return InvocationExpression(MarshalerTypeName(MarshalerType.Task), - ArgumentList(SingletonSeparatedList(Argument(MarshalerTypeName(_resultMarshalerType))))); + yield return statement; } - } - public override IEnumerable Generate(StubIdentifierContext context) - { var jsty = (JSTaskTypeInfo)((JSMarshallingInfo)TypeInfo.MarshallingAttributeInfo).TypeInfo; - string argName = context.GetAdditionalIdentifier(TypeInfo, "js_arg"); - var target = TypeInfo.IsManagedReturnPosition - ? Constants.ArgumentReturn - : argName; - - var source = TypeInfo.IsManagedReturnPosition - ? Argument(IdentifierName(context.GetIdentifiers(TypeInfo).native)) - : _inner.AsArgument(context); + var (managed, js) = context.GetIdentifiers(TypeInfo); if (context.CurrentStage == StubIdentifierContext.Stage.UnmarshalCapture && CodeContext.Direction == MarshalDirection.ManagedToUnmanaged && TypeInfo.IsManagedReturnPosition) { yield return jsty.ResultTypeInfo is JSSimpleTypeInfo(KnownManagedType.Void) - ? ToManagedMethodVoid(target, source) - : ToManagedMethod(target, source, jsty.ResultTypeInfo.Syntax); + ? ToManagedMethodVoid(js, Argument(IdentifierName(managed))) + : ToManagedMethod(js, Argument(IdentifierName(managed)), jsty.ResultTypeInfo.Syntax); } if (context.CurrentStage == StubIdentifierContext.Stage.Marshal && CodeContext.Direction == MarshalDirection.UnmanagedToManaged && TypeInfo.IsManagedReturnPosition) { yield return jsty.ResultTypeInfo is JSSimpleTypeInfo(KnownManagedType.Void) - ? ToJSMethodVoid(target, source) - : ToJSMethod(target, source, jsty.ResultTypeInfo.Syntax); - } - - foreach (var x in base.Generate(context)) - { - yield return x; + ? ToJSMethodVoid(js, Argument(IdentifierName(managed))) + : ToJSMethod(js, Argument(IdentifierName(managed)), jsty.ResultTypeInfo.Syntax); } if (context.CurrentStage == StubIdentifierContext.Stage.PinnedMarshal && CodeContext.Direction == MarshalDirection.ManagedToUnmanaged && !TypeInfo.IsManagedReturnPosition) { yield return jsty.ResultTypeInfo is JSSimpleTypeInfo(KnownManagedType.Void) - ? ToJSMethodVoid(target, source) - : ToJSMethod(target, source, jsty.ResultTypeInfo.Syntax); + ? ToJSMethodVoid(js, Argument(IdentifierName(managed))) + : ToJSMethod(js, Argument(IdentifierName(managed)), jsty.ResultTypeInfo.Syntax); } if (context.CurrentStage == StubIdentifierContext.Stage.Unmarshal && CodeContext.Direction == MarshalDirection.UnmanagedToManaged && !TypeInfo.IsManagedReturnPosition) { yield return jsty.ResultTypeInfo is JSSimpleTypeInfo(KnownManagedType.Void) - ? ToManagedMethodVoid(target, source) - : ToManagedMethod(target, source, jsty.ResultTypeInfo.Syntax); + ? ToManagedMethodVoid(js, Argument(IdentifierName(managed))) + : ToManagedMethod(js, Argument(IdentifierName(managed)), jsty.ResultTypeInfo.Syntax); } } - private ExpressionStatementSyntax ToManagedMethodVoid(string target, ArgumentSyntax source) + private static ExpressionStatementSyntax ToManagedMethodVoid(string target, ArgumentSyntax source) { return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(target), GetToManagedMethod(Type))) + IdentifierName(target), GetToManagedMethod(MarshalerType.Task))) .WithArgumentList(ArgumentList(SingletonSeparatedList(source.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)))))); } - private ExpressionStatementSyntax ToJSMethodVoid(string target, ArgumentSyntax source) + private static ExpressionStatementSyntax ToJSMethodVoid(string target, ArgumentSyntax source) { return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(target), GetToJSMethod(Type))) + IdentifierName(target), GetToJSMethod(MarshalerType.Task))) .WithArgumentList(ArgumentList(SingletonSeparatedList(source)))); } private ExpressionStatementSyntax ToManagedMethod(string target, ArgumentSyntax source, TypeSyntax sourceType) { return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(target), GetToManagedMethod(Type))) + IdentifierName(target), GetToManagedMethod(MarshalerType.Task))) .WithArgumentList(ArgumentList(SeparatedList(new[]{ source.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)), Argument(ParenthesizedLambdaExpression() @@ -111,7 +82,7 @@ private ExpressionStatementSyntax ToManagedMethod(string target, ArgumentSyntax .WithType(sourceType)}))) .WithBlock(Block(SingletonList(ExpressionStatement( InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("__task_result_arg"), GetToManagedMethod(_resultMarshalerType))) + IdentifierName("__task_result_arg"), GetToManagedMethod(resultMarshalerType))) .WithArgumentList(ArgumentList(SeparatedList(new[]{ Argument(IdentifierName("__task_result")).WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)), }))))))))})))); @@ -120,7 +91,7 @@ private ExpressionStatementSyntax ToManagedMethod(string target, ArgumentSyntax private ExpressionStatementSyntax ToJSMethod(string target, ArgumentSyntax source, TypeSyntax sourceType) { return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(target), GetToJSMethod(Type))) + IdentifierName(target), GetToJSMethod(MarshalerType.Task))) .WithArgumentList(ArgumentList(SeparatedList(new[]{ source, Argument(ParenthesizedLambdaExpression() @@ -133,7 +104,7 @@ private ExpressionStatementSyntax ToJSMethod(string target, ArgumentSyntax sourc .WithType(sourceType)}))) .WithBlock(Block(SingletonList(ExpressionStatement( InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("__task_result_arg"), GetToJSMethod(_resultMarshalerType))) + IdentifierName("__task_result_arg"), GetToJSMethod(resultMarshalerType))) .WithArgumentList(ArgumentList(SeparatedList(new[]{ Argument(IdentifierName("__task_result")), }))))))))})))); diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/VoidGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/VoidGenerator.cs deleted file mode 100644 index 2177f5436e431e..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/VoidGenerator.cs +++ /dev/null @@ -1,9 +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.JavaScript; - -namespace Microsoft.Interop.JavaScript -{ - internal sealed class VoidGenerator(TypePositionInfo info, StubCodeContext context, MarshalerType marshalerType) : BaseJSGenerator(marshalerType, new Forwarder().Bind(info, context)); -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/NoSpanAndTaskMixingResolver.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/NoSpanAndTaskMixingResolver.cs new file mode 100644 index 00000000000000..1d46d530a958ea --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/NoSpanAndTaskMixingResolver.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; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Interop.JavaScript +{ + internal sealed class NoSpanAndTaskMixingResolver : IMarshallingGeneratorResolver + { + private bool _hasSpan; + private bool _hasTask; + + public ResolvedGenerator Create(TypePositionInfo info, StubCodeContext context) + { + bool foundInteresting = false; + if (info.MarshallingAttributeInfo is JSMarshallingInfo(_, JSSpanTypeInfo)) + { + _hasSpan = true; + foundInteresting = true; + } + + if (info.MarshallingAttributeInfo is JSMarshallingInfo(_, JSTaskTypeInfo) && info.IsManagedReturnPosition) + { + _hasTask = true; + foundInteresting = true; + } + + if (foundInteresting && _hasSpan && _hasTask) + { + return ResolvedGenerator.NotSupported(info, context, + new GeneratorDiagnostic.NotSupported(info) + { + NotSupportedDetails = SR.SpanAndTaskNotSupported + }); + } + + return ResolvedGenerator.UnresolvedGenerator; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/SignatureBindingHelpers.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/SignatureBindingHelpers.cs new file mode 100644 index 00000000000000..d8a3a8231904fc --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/SignatureBindingHelpers.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.Collections.Immutable; +using System.Linq; +using System.Runtime.InteropServices.JavaScript; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace Microsoft.Interop.JavaScript +{ + internal static class SignatureBindingHelpers + { + public static ArgumentSyntax CreateSignaturesArgument(ImmutableArray elements, StubCodeContext context) + { + List arguments = []; + + foreach (TypePositionInfo element in elements.Where(e => e.NativeIndex != TypePositionInfo.UnsetIndex).OrderBy(e => e.NativeIndex)) + { + var (baseType, subTypes) = JSGeneratorResolver.GetMarshallerTypeForBinding(element, context); + ExpressionSyntax bindSyntax = MarshalerTypeName(baseType); + if (subTypes is not null) + { + bindSyntax = InvocationExpression(bindSyntax, + ArgumentList(SeparatedList(subTypes.Select(s => Argument(MarshalerTypeName(s)))))); + } + arguments.Add(ExpressionElement(bindSyntax)); + } + + return Argument(CollectionExpression(SeparatedList(arguments))); + } + + private static IdentifierNameSyntax MarshalerTypeName(MarshalerType marshalerType) + { + return IdentifierName(Constants.JSMarshalerTypeGlobalDot + marshalerType.ToString()); + } + + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs index f6be095eecab92..7f1447673f85e6 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs @@ -261,19 +261,22 @@ internal static unsafe void DispatchJSFunctionSync(JSObject jsFunction, Span arguments) { - var args = (nint)Unsafe.AsPointer(ref arguments[0]); - var sig = (nint)signature.Header; + fixed (JSMarshalerArgument* argsPtr = arguments) + { + var args = (nint)argsPtr; + var sig = (nint)signature.Header; - ref JSMarshalerArgument exc = ref arguments[0]; + ref JSMarshalerArgument exc = ref arguments[0]; - // we already know that we are not on the right thread - // this will be blocking until resolved by that thread - Interop.Runtime.InvokeJSImportSyncSend(targetContext.JSNativeTID, sig, args); + // we already know that we are not on the right thread + // this will be blocking until resolved by that thread + Interop.Runtime.InvokeJSImportSyncSend(targetContext.JSNativeTID, sig, args); - if (exc.slot.Type != MarshalerType.None) - { - JSHostImplementation.ThrowException(ref exc); + if (exc.slot.Type != MarshalerType.None) + { + JSHostImplementation.ThrowException(ref exc); + } } } @@ -412,8 +418,7 @@ internal static unsafe void DispatchJSImportAsyncPost(JSFunctionBinding signatur var bytes = sizeof(JSMarshalerArgument) * arguments.Length; void* cpy = (void*)Marshal.AllocHGlobal(bytes); - void* src = Unsafe.AsPointer(ref arguments[0]); - Unsafe.CopyBlock(cpy, src, (uint)bytes); + arguments.CopyTo(new Span(cpy, arguments.Length)); var sig = (nint)signature.Header; // we already know that we are not on the right thread @@ -476,8 +481,7 @@ internal static unsafe void ResolveOrRejectPromise(JSProxyContext targetContext, // this copy is freed in mono_wasm_resolve_or_reject_promise var bytes = sizeof(JSMarshalerArgument) * arguments.Length; void* cpy = (void*)Marshal.AllocHGlobal(bytes); - void* src = Unsafe.AsPointer(ref arguments[0]); - Unsafe.CopyBlock(cpy, src, (uint)bytes); + arguments.CopyTo(new Span(cpy, arguments.Length)); // async Interop.Runtime.ResolveOrRejectPromisePost(targetContext.JSNativeTID, (nint)cpy); diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Compiles.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Compiles.cs index 466cd3072f1ef4..b376bb096b6fb1 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Compiles.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Compiles.cs @@ -51,7 +51,7 @@ public void ValidateSnippets(string source) } [Fact] - public async Task ValidateGeneratedSourceOutput() + public async Task ValidateGeneratedSourceOutput_AllAnnotatedParameters() { var test = new Test() { @@ -71,70 +71,97 @@ internal static partial void Annotated(object a1, long a2, long a3, global::Syst { if (__signature_Annotated_1583225186 == null) { - __signature_Annotated_1583225186 = global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.BindJSFunction("DoesNotExist", null, new global::System.Runtime.InteropServices.JavaScript.JSMarshalerType[] { global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Discard, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int52, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.BigInt64, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Action(), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Function(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int32), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Span(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Byte), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.ArraySegment(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Byte), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Array(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTime, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTimeOffset, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTime), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTimeOffset), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int52), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.BigInt64) }); + __signature_Annotated_1583225186 = global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.BindJSFunction("DoesNotExist", null, [global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Discard, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int52, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.BigInt64, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Action(), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Function(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int32), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Span(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Byte), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.ArraySegment(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Byte), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Array(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTime, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTimeOffset, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTime), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTimeOffset), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int52), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.BigInt64)]); } - global::System.Span __arguments_buffer = stackalloc global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument[17]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __arg_exception = ref __arguments_buffer[0]; - __arg_exception.Initialize(); - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __arg_return = ref __arguments_buffer[1]; - __arg_return.Initialize(); - // Setup - Perform required setup. - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a15_native__js_arg = ref __arguments_buffer[16]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a14_native__js_arg = ref __arguments_buffer[15]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a13_native__js_arg = ref __arguments_buffer[14]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a12_native__js_arg = ref __arguments_buffer[13]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a11_native__js_arg = ref __arguments_buffer[12]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a10_native__js_arg = ref __arguments_buffer[11]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a9_native__js_arg = ref __arguments_buffer[10]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a8_native__js_arg = ref __arguments_buffer[9]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a7_native__js_arg = ref __arguments_buffer[8]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a6_native__js_arg = ref __arguments_buffer[7]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a5_native__js_arg = ref __arguments_buffer[6]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a4_native__js_arg = ref __arguments_buffer[5]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a3_native__js_arg = ref __arguments_buffer[4]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a2_native__js_arg = ref __arguments_buffer[3]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a1_native__js_arg = ref __arguments_buffer[2]; - // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned. - __a15_native__js_arg.ToJS(a15, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, long __task_result) => { - __task_result_arg.ToJSBig(__task_result); - }); - __a14_native__js_arg.ToJS(a14, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, long __task_result) => - { - __task_result_arg.ToJS(__task_result); - }); - __a13_native__js_arg.ToJS(a13, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, global::System.DateTimeOffset __task_result) => - { - __task_result_arg.ToJS(__task_result); - }); - __a12_native__js_arg.ToJS(a12, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, global::System.DateTime __task_result) => - { - __task_result_arg.ToJS(__task_result); - }); - __a11_native__js_arg.ToJS(a11); - __a10_native__js_arg.ToJS(a10); - __a9_native__js_arg.ToJS(a9); - __a8_native__js_arg.ToJS(a8, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, object __task_result) => - { - __task_result_arg.ToJS(__task_result); - }); - __a7_native__js_arg.ToJS(a7); - __a6_native__js_arg.ToJS(a6); - __a5_native__js_arg.ToJS(a5, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __delegate_arg_arg1, int __delegate_arg1) => + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument ____arg_exception_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument ____arg_return_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a1_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a2_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a3_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a4_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a5_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a6_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a7_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a8_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a9_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a10_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a11_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a12_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a13_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a14_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a15_native; + // Setup - Perform required setup. + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out ____arg_return_native); + ____arg_return_native.Initialize(); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out ____arg_exception_native); + ____arg_exception_native.Initialize(); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a15_native); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a14_native); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a13_native); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a12_native); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a11_native); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a10_native); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a9_native); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a8_native); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a7_native); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a6_native); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a5_native); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a4_native); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a3_native); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a2_native); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __a1_native); + // Marshal - Convert managed data to native data. + __a11_native.ToJS(a11); + __a10_native.ToJS(a10); + __a9_native.ToJS(a9); + __a7_native.ToJS(a7); + __a6_native.ToJS(a6); + __a3_native.ToJSBig(a3); + __a2_native.ToJS(a2); + __a1_native.ToJS(a1); + { + // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned. + __a15_native.ToJS(a15, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, long __task_result) => + { + __task_result_arg.ToJSBig(__task_result); + }); + __a14_native.ToJS(a14, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, long __task_result) => + { + __task_result_arg.ToJS(__task_result); + }); + __a13_native.ToJS(a13, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, global::System.DateTimeOffset __task_result) => + { + __task_result_arg.ToJS(__task_result); + }); + __a12_native.ToJS(a12, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, global::System.DateTime __task_result) => + { + __task_result_arg.ToJS(__task_result); + }); + __a8_native.ToJS(a8, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, object __task_result) => + { + __task_result_arg.ToJS(__task_result); + }); + __a5_native.ToJS(a5, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __delegate_arg_arg1, int __delegate_arg1) => + { + __delegate_arg_arg1.ToJS(__delegate_arg1); + }); + __a4_native.ToJS(a4); + __InvokeJSFunction(____arg_exception_native, ____arg_return_native, __a1_native, __a2_native, __a3_native, __a4_native, __a5_native, __a6_native, __a7_native, __a8_native, __a9_native, __a10_native, __a11_native, __a12_native, __a13_native, __a14_native, __a15_native); + } + } + + [global::System.Diagnostics.DebuggerNonUserCode] + void __InvokeJSFunction(global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument ____arg_exception_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument ____arg_return_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a1_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a2_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a3_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a4_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a5_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a6_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a7_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a8_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a9_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a10_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a11_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a12_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a13_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a14_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a15_native) { - __delegate_arg_arg1.ToJS(__delegate_arg1); - }); - __a4_native__js_arg.ToJS(a4); - __a3_native__js_arg.ToJSBig(a3); - __a2_native__js_arg.ToJS(a2); - __a1_native__js_arg.ToJS(a1); - global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.InvokeJS(__signature_Annotated_1583225186, __arguments_buffer); + global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.InvokeJS(__signature_Annotated_1583225186, [____arg_exception_native, ____arg_return_native, __a1_native, __a2_native, __a3_native, __a4_native, __a5_native, __a6_native, __a7_native, __a8_native, __a9_native, __a10_native, __a11_native, __a12_native, __a13_native, __a14_native, __a15_native]); + } } static global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding __signature_Annotated_1583225186; } - + """.ReplaceLineEndings("\r\n"), Encoding.UTF8)), (typeof(Microsoft.Interop.JavaScript.JSExportGenerator), "JSExports.g.cs", @@ -158,7 +185,7 @@ static void __Register_() if (initialized || global::System.Runtime.InteropServices.RuntimeInformation.OSArchitecture != global::System.Runtime.InteropServices.Architecture.Wasm) return; initialized = true; - global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.BindManagedFunction("[TestProject]Basic:AnnotatedExport", 1583225186, new global::System.Runtime.InteropServices.JavaScript.JSMarshalerType[] { global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Discard, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int52, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.BigInt64, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Action(), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Function(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int32), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Span(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Byte), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.ArraySegment(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Byte), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Array(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTime, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTimeOffset, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTime), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTimeOffset), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int52), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.BigInt64) }); + global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.BindManagedFunction("[TestProject]Basic:AnnotatedExport", 1583225186, [global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Discard, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int52, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.BigInt64, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Action(), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Function(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int32), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Span(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Byte), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.ArraySegment(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Byte), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Array(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTime, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTimeOffset, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTime), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTimeOffset), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int52), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.BigInt64)]); } } } @@ -167,84 +194,195 @@ unsafe partial class Basic [global::System.Diagnostics.DebuggerNonUserCode] internal static unsafe void __Wrapper_AnnotatedExport_1583225186(global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument* __arguments_buffer) { - object a1; - long a2; - long a3; - global::System.Action a4; - global::System.Func a5; - global::System.Span a6; - global::System.ArraySegment a7; - global::System.Threading.Tasks.Task a8; - object[] a9; - global::System.DateTime a10; - global::System.DateTimeOffset a11; - global::System.Threading.Tasks.Task a12; - global::System.Threading.Tasks.Task a13; - global::System.Threading.Tasks.Task a14; - global::System.Threading.Tasks.Task a15; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __arg_exception = ref __arguments_buffer[0]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __arg_return = ref __arguments_buffer[1]; - // Setup - Perform required setup. - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a15_native__js_arg = ref __arguments_buffer[16]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a14_native__js_arg = ref __arguments_buffer[15]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a13_native__js_arg = ref __arguments_buffer[14]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a12_native__js_arg = ref __arguments_buffer[13]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a11_native__js_arg = ref __arguments_buffer[12]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a10_native__js_arg = ref __arguments_buffer[11]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a9_native__js_arg = ref __arguments_buffer[10]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a8_native__js_arg = ref __arguments_buffer[9]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a7_native__js_arg = ref __arguments_buffer[8]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a6_native__js_arg = ref __arguments_buffer[7]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a5_native__js_arg = ref __arguments_buffer[6]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a4_native__js_arg = ref __arguments_buffer[5]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a3_native__js_arg = ref __arguments_buffer[4]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a2_native__js_arg = ref __arguments_buffer[3]; - ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a1_native__js_arg = ref __arguments_buffer[2]; - try + __Stub(__arguments_buffer[2], __arguments_buffer[3], __arguments_buffer[4], __arguments_buffer[5], __arguments_buffer[6], __arguments_buffer[7], __arguments_buffer[8], __arguments_buffer[9], __arguments_buffer[10], __arguments_buffer[11], __arguments_buffer[12], __arguments_buffer[13], __arguments_buffer[14], __arguments_buffer[15], __arguments_buffer[16], __arguments_buffer); + [global::System.Diagnostics.DebuggerNonUserCode] + void __Stub(global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a1_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a2_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a3_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a4_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a5_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a6_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a7_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a8_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a9_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a10_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a11_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a12_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a13_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a14_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __a15_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument* ____arg_exception_native__param) { - // Unmarshal - Convert native data to managed data. - __a15_native__js_arg.ToManaged(out a15, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, out long __task_result) => + object a1 = default; + long a2 = default; + long a3 = default; + global::System.Action a4 = default; + global::System.Func a5 = default; + global::System.Span a6 = default; + global::System.ArraySegment a7 = default; + global::System.Threading.Tasks.Task a8 = default; + object[] a9 = default; + global::System.DateTime a10 = default; + global::System.DateTimeOffset a11 = default; + global::System.Threading.Tasks.Task a12 = default; + global::System.Threading.Tasks.Task a13 = default; + global::System.Threading.Tasks.Task a14 = default; + global::System.Threading.Tasks.Task a15 = default; + ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument ____arg_exception_native = ref *____arg_exception_native__param; + try { - __task_result_arg.ToManagedBig(out __task_result); - }); - __a14_native__js_arg.ToManaged(out a14, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, out long __task_result) => + // UnmarshalCapture - Capture the native data into marshaller instances in case conversion to managed data throws an exception. + __a11_native.ToManaged(out a11); + __a10_native.ToManaged(out a10); + __a9_native.ToManaged(out a9); + __a7_native.ToManaged(out a7); + __a6_native.ToManaged(out a6); + __a3_native.ToManagedBig(out a3); + __a2_native.ToManaged(out a2); + __a1_native.ToManaged(out a1); + // Unmarshal - Convert native data to managed data. + __a15_native.ToManaged(out a15, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, out long __task_result) => + { + __task_result_arg.ToManagedBig(out __task_result); + }); + __a14_native.ToManaged(out a14, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, out long __task_result) => + { + __task_result_arg.ToManaged(out __task_result); + }); + __a13_native.ToManaged(out a13, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, out global::System.DateTimeOffset __task_result) => + { + __task_result_arg.ToManaged(out __task_result); + }); + __a12_native.ToManaged(out a12, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, out global::System.DateTime __task_result) => + { + __task_result_arg.ToManaged(out __task_result); + }); + __a8_native.ToManaged(out a8, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, out object __task_result) => + { + __task_result_arg.ToManaged(out __task_result); + }); + __a5_native.ToManaged(out a5, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __delegate_arg_arg1, out int __delegate_arg1) => + { + __delegate_arg_arg1.ToManaged(out __delegate_arg1); + }); + __a4_native.ToManaged(out a4); + Basic.AnnotatedExport(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); + } + catch (global::System.Exception __arg_exception) { - __task_result_arg.ToManaged(out __task_result); - }); - __a13_native__js_arg.ToManaged(out a13, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, out global::System.DateTimeOffset __task_result) => - { - __task_result_arg.ToManaged(out __task_result); - }); - __a12_native__js_arg.ToManaged(out a12, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, out global::System.DateTime __task_result) => + ____arg_exception_native.ToJS(__arg_exception); + } + } + } + } + + """.ReplaceLineEndings("\r\n"), Encoding.UTF8)), + } + }, + }; + + await test.RunAsync(); + } + + [Fact] + public async Task ValidateGeneratedSourceOutput_Return() + { + var test = new Test() + { + TestState = + { + Sources = { CodeSnippets.DefaultReturnMarshaler("System.Threading.Tasks.Task") }, + GeneratedSources = + { + (typeof(Microsoft.Interop.JavaScript.JSImportGenerator), + "JSImports.g.cs", + SourceText.From(""" + // + unsafe partial class Basic + { + [global::System.Diagnostics.DebuggerNonUserCode] + public static partial global::System.Threading.Tasks.Task Import1() + { + if (__signature_Import1_622134597 == null) + { + __signature_Import1_622134597 = global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.BindJSFunction("DoesNotExist", null, [global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int32)]); + } + + { + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument ____arg_exception_native; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument ____arg_return_native; + global::System.Threading.Tasks.Task __retVal; + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __retVal_native; + // Setup - Perform required setup. + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out ____arg_return_native); + ____arg_return_native.Initialize(); + global::System.Runtime.CompilerServices.Unsafe.SkipInit(out ____arg_exception_native); + ____arg_exception_native.Initialize(); { - __task_result_arg.ToManaged(out __task_result); - }); - __a11_native__js_arg.ToManaged(out a11); - __a10_native__js_arg.ToManaged(out a10); - __a9_native__js_arg.ToManaged(out a9); - __a8_native__js_arg.ToManaged(out a8, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, out object __task_result) => + __retVal_native = __InvokeJSFunction(____arg_exception_native, ____arg_return_native); + } + + // UnmarshalCapture - Capture the native data into marshaller instances in case conversion to managed data throws an exception. + __retVal_native.ToManaged(out __retVal, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, out int __task_result) => { __task_result_arg.ToManaged(out __task_result); }); - __a7_native__js_arg.ToManaged(out a7); - __a6_native__js_arg.ToManaged(out a6); - __a5_native__js_arg.ToManaged(out a5, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __delegate_arg_arg1, out int __delegate_arg1) => - { - __delegate_arg_arg1.ToManaged(out __delegate_arg1); - }); - __a4_native__js_arg.ToManaged(out a4); - __a3_native__js_arg.ToManagedBig(out a3); - __a2_native__js_arg.ToManaged(out a2); - __a1_native__js_arg.ToManaged(out a1); - Basic.AnnotatedExport(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); + return __retVal; } - catch (global::System.Exception ex) + + [global::System.Diagnostics.DebuggerNonUserCode] + global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __InvokeJSFunction(global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument ____arg_exception_native, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument ____arg_return_native) { - __arg_exception.ToJS(ex); + global::System.Span __arguments_buffer = [____arg_exception_native, ____arg_return_native]; + global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.InvokeJS(__signature_Import1_622134597, __arguments_buffer); + return __arguments_buffer[1]; } } + + static global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding __signature_Import1_622134597; } - + + """.ReplaceLineEndings("\r\n"), Encoding.UTF8)), + (typeof(Microsoft.Interop.JavaScript.JSExportGenerator), + "JSExports.g.cs", + SourceText.From(""" + // + namespace System.Runtime.InteropServices.JavaScript + { + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute] + unsafe class __GeneratedInitializer + { + [global::System.ThreadStaticAttribute] + static bool initialized; + [global::System.Runtime.CompilerServices.ModuleInitializerAttribute, global::System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicMethods, "System.Runtime.InteropServices.JavaScript.__GeneratedInitializer", "TestProject")] + static internal void __TrimmingPreserve_() + { + } + + [global::System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute("__Wrapper_Export1_622134597", "Basic", "TestProject")] + static void __Register_() + { + if (initialized || global::System.Runtime.InteropServices.RuntimeInformation.OSArchitecture != global::System.Runtime.InteropServices.Architecture.Wasm) + return; + initialized = true; + global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.BindManagedFunction("[TestProject]Basic:Export1", 622134597, [global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int32)]); + } + } + } + unsafe partial class Basic + { + [global::System.Diagnostics.DebuggerNonUserCode] + internal static unsafe void __Wrapper_Export1_622134597(global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument* __arguments_buffer) + { + __Stub(__arguments_buffer, __arguments_buffer + 1); + [global::System.Diagnostics.DebuggerNonUserCode] + void __Stub(global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument* ____arg_exception_native__param, global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument* __invokeRetValUnmanaged__param) + { + ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument ____arg_exception_native = ref *____arg_exception_native__param; + ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __invokeRetValUnmanaged = ref *__invokeRetValUnmanaged__param; + global::System.Threading.Tasks.Task __invokeRetVal = default; + try + { + __invokeRetVal = Basic.Export1(); + // Marshal - Convert managed data to native data. + __invokeRetValUnmanaged.ToJS(__invokeRetVal, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, int __task_result) => + { + __task_result_arg.ToJS(__task_result); + }); + } + catch (global::System.Exception __arg_exception) + { + ____arg_exception_native.ToJS(__arg_exception); + } + } + } + } + """.ReplaceLineEndings("\r\n"), Encoding.UTF8)), } }, diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs index c247ef332921f3..278270b470b871 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs @@ -490,6 +490,7 @@ await executor.Execute(async () => } [Theory, MemberData(nameof(GetTargetThreadsAndBlockingCalls))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/99951")] public async Task WaitInAsyncAssertsOnlyOnJSWebWorker(Executor executor, NamedCall method) { using var cts = CreateTestCaseTimeoutSource(); diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/Strings.resx index 55fbc3b4247ede..7253540e16a0df 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/Strings.resx @@ -180,8 +180,8 @@ The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type. - - All marshallers for values that are passed as the unmanaged return value must have the same unmanaged type. + + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. Containing type '{0}' has accessibility '{1}'. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.cs.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.cs.xlf index 08e92ac26a5c47..28fedf962b6b43 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.cs.xlf @@ -842,9 +842,9 @@ Typ vstupního bodu řadiče „{0}“ pro spravovaný typ „{1}“ musí mít aritu o jednu větší než spravovaný typ. - - All marshallers for values that are passed as the unmanaged return value must have the same unmanaged type. - Všechny zařazovací moduly pro hodnoty, které jsou předány jako nespravovaná návratová hodnota, musí mít stejný nespravovaný typ. + + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.de.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.de.xlf index 787fa64fdcc10d..d6e696e7869ecf 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.de.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.de.xlf @@ -842,9 +842,9 @@ Der Marshaller-Einstiegspunkttyp "{0}" für den verwalteten Typ "{1}" muss eine Stelligkeit aufweisen, die größer als der verwaltete Typ ist. - - All marshallers for values that are passed as the unmanaged return value must have the same unmanaged type. - Alle Marshaller für Werte, die als nicht verwalteter Rückgabewert übergeben werden, müssen denselben nicht verwalteten Typ aufweisen. + + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.es.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.es.xlf index 40fe7056997fcc..047562a0f5d5f7 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.es.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.es.xlf @@ -842,9 +842,9 @@ El tipo de punto de entrada del serializador "{0}" para el tipo administrado "{1}" debe tener una aridad mayor que el tipo administrado. - - All marshallers for values that are passed as the unmanaged return value must have the same unmanaged type. - Todos los serializadores para los valores que se pasan como valor devuelto no administrado deberán tener el mismo tipo no administrado. + + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.fr.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.fr.xlf index 8fdb9e66b2f816..bfef83e646d4c4 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.fr.xlf @@ -842,9 +842,9 @@ Le type de point d’entrée marshaleur '{0}' pour le type managé '{1}' doit avoir une arité supérieure au type managé. - - All marshallers for values that are passed as the unmanaged return value must have the same unmanaged type. - Tous les marshaleurs pour les valeurs passées en tant que valeur de retour non managée doivent avoir le même type non managé. + + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.it.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.it.xlf index 2a5f152ed18c65..e69220e9490ac6 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.it.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.it.xlf @@ -842,9 +842,9 @@ Il tipo di punto di ingresso del marshalling '{0}' per il tipo gestito '{1}' deve avere un grado maggiore rispetto a quello del tipo gestito. - - All marshallers for values that are passed as the unmanaged return value must have the same unmanaged type. - Tutti i marshalling per i valori passati come valore restituito non gestito devono avere lo stesso tipo non gestito. + + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ja.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ja.xlf index 999325fdc4e6b4..78a71157862438 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ja.xlf @@ -842,9 +842,9 @@ マネージド型 '{1}' のマーシャラー エントリ ポイント型 '{0}' には、マネージド型より 1 大きい引数が必要です。 - - All marshallers for values that are passed as the unmanaged return value must have the same unmanaged type. - アンマネージ戻り値として渡される値のすべてのマーシャラーは、同じアンマネージ型を持つ必要があります。 + + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ko.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ko.xlf index 10f9cf84afe3ab..7ba79359c4e9bb 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ko.xlf @@ -842,9 +842,9 @@ 관리 유형 '{1}'에 대한 마샬러 진입점 유형 '{0}'에는 관리 유형보다 1이 더 커야 합니다. - - All marshallers for values that are passed as the unmanaged return value must have the same unmanaged type. - 관리되지 않는 반환 값으로 전달되는 값에 대한 모든 마샬러는 관리되지 않는 형식이 동일해야 합니다. + + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pl.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pl.xlf index 55c27cde0e5387..b3201d69110f3e 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pl.xlf @@ -842,9 +842,9 @@ Typ punktu wejścia marshallera „{0}” dla typu zarządzanego „{1}” musi mieć liczbę argumentów o jeden większą niż typ zarządzany. - - All marshallers for values that are passed as the unmanaged return value must have the same unmanaged type. - Wszystkie elementy organizujące dla wartości przekazywanych jako niezarządzana wartość zwracana muszą mieć ten sam typ niezarządzany. + + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. @@ -1321,4 +1321,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pt-BR.xlf index 9446ca11ff9b27..6ff06e10672ba1 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pt-BR.xlf @@ -842,9 +842,9 @@ O tipo de ponto de entrada para realizar marshaling '{0}' para o tipo gerenciado '{1}' deve ter uma aridade maior do que o tipo gerenciado. - - All marshallers for values that are passed as the unmanaged return value must have the same unmanaged type. - Todos os marshallers de valores passados como o valor retornado não gerenciado devem ter o mesmo tipo não gerenciado. + + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ru.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ru.xlf index 394a0e09d2a127..0c6446ff0d2715 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ru.xlf @@ -842,9 +842,9 @@ Тип точки входа маршаллера "{0}" для управляемого типа "{1}" должен иметь более высокую арность, чем управляемый тип. - - All marshallers for values that are passed as the unmanaged return value must have the same unmanaged type. - Все маршалеры для значений, передаваемых в качестве неуправляемого возвращаемого значения, должны использовать одинаковый неуправляемый тип. + + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.tr.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.tr.xlf index 6d7c166203d76d..3a4095b184df76 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.tr.xlf @@ -842,9 +842,9 @@ Yönetilen tür '{1}' için sıralayıcı giriş noktası '{0}', yönetilen türden büyük bir parametre sayısına sahip olmalıdır. - - All marshallers for values that are passed as the unmanaged return value must have the same unmanaged type. - Yönetilmeyen dönüş değeri olarak geçirilen değerler için tüm hazırlayıcılar aynı yönetilmeyen türe sahip olmalıdır. + + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hans.xlf index 2df445d4f9feba..8290f2528c338c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hans.xlf @@ -842,9 +842,9 @@ 托管类型“{1}”的封送程序入口点类型“{0}”必须具有大于托管类型的 arity。 - - All marshallers for values that are passed as the unmanaged return value must have the same unmanaged type. - 作为非托管返回值传递的值的所有封送程序必须具有相同的非托管类型。 + + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hant.xlf index 07248b19e3e7e5..e98e23423531a2 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hant.xlf @@ -842,9 +842,9 @@ 受控類型 '{1}' 的封送處理器進入點類型 '{0}' 必須具有大於受控類型的 arity。 - - All marshallers for values that are passed as the unmanaged return value must have the same unmanaged type. - 以非受控傳回值傳遞之值的所有封送處理常式,都必須具有相同的非受控類型。 + + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. + All marshallers for values that are passed as the unmanaged return value or parameter must have the same unmanaged type. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BoundGenerators.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BoundGenerators.cs index 335aca92cab2a9..c517a0c0353999 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BoundGenerators.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BoundGenerators.cs @@ -66,29 +66,60 @@ public static BoundGenerators Create(ImmutableArray elementTyp } } - // Sort the parameter marshallers by index to ensure that we handle them in order when producing signatures. - managedParamMarshallers.Sort(static (m1, m2) => m1.TypeInfo.ManagedIndex.CompareTo(m2.TypeInfo.ManagedIndex)); - nativeParamMarshallers.Sort(static (m1, m2) => m1.TypeInfo.NativeIndex.CompareTo(m2.TypeInfo.NativeIndex)); // Now that we've processed all of the signature marshallers, // we'll handle the special ones that might depend on them, like the exception marshaller. if (managedExceptionInfo is not null) { + // The managed exception marshaller may overlap with another marshaller in the native position. + // In that case, we need to validate some additional invariants. + // Also, some cases may require an overlap, such as when using the "COM" exception marshalling. + IBoundMarshallingGenerator? overlappedMarshaller = null; + if (managedExceptionInfo.IsNativeReturnPosition) + { + overlappedMarshaller = nativeReturnMarshaller; + } + else if (managedExceptionInfo.NativeIndex is not (TypePositionInfo.UnsetIndex or TypePositionInfo.ExceptionIndex)) + { + overlappedMarshaller = nativeParamMarshallers.FirstOrDefault(e => e.TypeInfo.NativeIndex == managedExceptionInfo.NativeIndex); + } + if (managedExceptionInfo.MarshallingAttributeInfo is ComExceptionMarshalling) { - managedExceptionInfo = managedExceptionInfo with + if (overlappedMarshaller is null) + { + generatorDiagnostics.Add(new GeneratorDiagnostic.NotSupported(managedExceptionInfo)); + } + else { - MarshallingAttributeInfo = ComExceptionMarshalling.CreateSpecificMarshallingInfo(nativeReturnMarshaller.NativeType) - }; + managedExceptionInfo = managedExceptionInfo with + { + MarshallingAttributeInfo = ComExceptionMarshalling.CreateSpecificMarshallingInfo(overlappedMarshaller.NativeType) + }; + } } - IMarshallingGeneratorResolver exceptionHandlerFactory = new ExtendedInvariantsValidator(nativeReturnMarshaller.NativeType, generatorResolver); + IMarshallingGeneratorResolver exceptionHandlerFactory = generatorResolver; + + if (overlappedMarshaller is not null) + { + exceptionHandlerFactory = new MatchingNativeTypeValidator(overlappedMarshaller.NativeType, exceptionHandlerFactory); + } - // We explicitly don't include exceptionMarshaller in the signatureMarshallers collection - // as it needs to be specially emitted. managedExceptionMarshaller = CreateGenerator(managedExceptionInfo, generatorResolver); + + if (overlappedMarshaller is null && !TypePositionInfo.IsSpecialIndex(managedExceptionInfo.NativeIndex)) + { + // If the exception marshaller doesn't overlap with another marshaller but has a native index, + // we need to add it to the list of native parameter marshallers. + nativeParamMarshallers.Add(managedExceptionMarshaller); + } } + // Sort the parameter marshallers by index to ensure that we handle them in order when producing signatures. + managedParamMarshallers.Sort(static (m1, m2) => m1.TypeInfo.ManagedIndex.CompareTo(m2.TypeInfo.ManagedIndex)); + nativeParamMarshallers.Sort(static (m1, m2) => m1.TypeInfo.NativeIndex.CompareTo(m2.TypeInfo.NativeIndex)); + generatorBindingDiagnostics = generatorDiagnostics.ToImmutable(); return new BoundGenerators() @@ -134,7 +165,7 @@ public static BoundGenerators Create(ImmutableArray elementTyp { return Array.Empty<(bool, int)>(); } - return MarshallerHelpers.GetDependentElementsOfMarshallingInfo(info.MarshallingAttributeInfo) + return info.MarshallingAttributeInfo.ElementDependencies .Select(static info => GetInfoIndex(info)).ToImmutableArray(); } @@ -190,31 +221,24 @@ IBoundMarshallingGenerator CreateGenerator(TypePositionInfo p, IMarshallingGener public bool HasManagedExceptionMarshaller => !ManagedExceptionMarshaller.IsForwarder(); - private sealed class ExtendedInvariantsValidator : IMarshallingGeneratorResolver + /// + /// Validate that the resolved generator resolves to the same native type. + /// + private sealed class MatchingNativeTypeValidator(ManagedTypeInfo requiredNativeType, IMarshallingGeneratorResolver inner) : IMarshallingGeneratorResolver { - private readonly ManagedTypeInfo _nativeReturnType; - private readonly IMarshallingGeneratorResolver _inner; - - public ExtendedInvariantsValidator(ManagedTypeInfo nativeReturnType, IMarshallingGeneratorResolver inner) - { - _nativeReturnType = nativeReturnType; - _inner = inner; - } - public ResolvedGenerator Create(TypePositionInfo info, StubCodeContext context) { - ResolvedGenerator generator = _inner.Create(info, context); + ResolvedGenerator generator = inner.Create(info, context); if (!generator.IsResolvedWithoutErrors) { return generator; } // Marshallers that share the native return position must have the same native return type. - if (info.IsNativeReturnPosition - && generator.Generator.NativeType != _nativeReturnType) + if (generator.Generator.NativeType != requiredNativeType) { return ResolvedGenerator.NotSupported(info, context, new(info) { - NotSupportedDetails = SR.MarshallerInNativeReturnPositionMustMatchNativeReturnType + NotSupportedDetails = SR.MarshallerInOverlappingNativePositionMustMatchNativeType }); } return generator; diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index d37849547d1a21..45b1ad734483dd 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -229,33 +229,6 @@ public bool AnyIncomingEdge(int to) } } - public static IEnumerable GetDependentElementsOfMarshallingInfo( - MarshallingInfo elementMarshallingInfo) - { - if (elementMarshallingInfo is NativeLinearCollectionMarshallingInfo nestedCollection) - { - if (nestedCollection.ElementCountInfo is CountElementCountInfo { ElementInfo: TypePositionInfo nestedCountElement }) - { - // Do not include dependent elements with no managed or native index. - // These values are dummy values that are inserted earlier to avoid emitting extra diagnostics. - if (nestedCountElement.ManagedIndex != TypePositionInfo.UnsetIndex || nestedCountElement.NativeIndex != TypePositionInfo.UnsetIndex) - { - yield return nestedCountElement; - } - } - foreach (KeyValuePair mode in nestedCollection.Marshallers.Modes) - { - foreach (TypePositionInfo nestedElement in GetDependentElementsOfMarshallingInfo(mode.Value.CollectionElementMarshallingInfo)) - { - if (nestedElement.ManagedIndex != TypePositionInfo.UnsetIndex || nestedElement.NativeIndex != TypePositionInfo.UnsetIndex) - { - yield return nestedElement; - } - } - } - } - } - // private static readonly InvocationExpressionSyntax SkipInitInvocation = public static StatementSyntax SkipInitOrDefaultInit(TypePositionInfo info, StubIdentifierContext context) { diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs index 36f56c08bc72c1..6dcc8b97fcedb3 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.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 Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -35,6 +36,8 @@ public abstract record MarshallingInfo { protected MarshallingInfo() { } + + public virtual IEnumerable ElementDependencies => []; } /// @@ -117,7 +120,40 @@ public sealed record NativeLinearCollectionMarshallingInfo( CountInfo ElementCountInfo, ManagedTypeInfo PlaceholderTypeParameter) : NativeMarshallingAttributeInfo( EntryPointType, - Marshallers); + Marshallers) + { + public override IEnumerable ElementDependencies + { + get + { + return field ??= GetElementDependencies().ToImmutableArray(); + + IEnumerable GetElementDependencies() + { + if (ElementCountInfo is CountElementCountInfo { ElementInfo: TypePositionInfo nestedCountElement }) + { + // Do not include dependent elements with no managed or native index. + // These values are dummy values that are inserted earlier to avoid emitting extra diagnostics. + if (nestedCountElement.ManagedIndex != TypePositionInfo.UnsetIndex || nestedCountElement.NativeIndex != TypePositionInfo.UnsetIndex) + { + yield return nestedCountElement; + } + } + + foreach (KeyValuePair mode in Marshallers.Modes) + { + foreach (TypePositionInfo nestedElement in mode.Value.CollectionElementMarshallingInfo.ElementDependencies) + { + if (nestedElement.ManagedIndex != TypePositionInfo.UnsetIndex || nestedElement.NativeIndex != TypePositionInfo.UnsetIndex) + { + yield return nestedElement; + } + } + } + } + } + } + } /// /// Marshal an exception based on the same rules as the built-in COM system based on the unmanaged type of the native return marshaller. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/UnmanagedToManagedStubGenerator.cs similarity index 98% rename from src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs rename to src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/UnmanagedToManagedStubGenerator.cs index 04897fb03d7630..bdc27c6555ea19 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/UnmanagedToManagedStubGenerator.cs @@ -11,7 +11,7 @@ namespace Microsoft.Interop { - internal sealed class UnmanagedToManagedStubGenerator + public sealed class UnmanagedToManagedStubGenerator { private const string ReturnIdentifier = "__retVal"; diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/VariableDeclarations.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/VariableDeclarations.cs index fb10fd2f74aaed..db65df307c5eb2 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/VariableDeclarations.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/VariableDeclarations.cs @@ -164,7 +164,9 @@ static void AppendVariableDeclarations(ImmutableArrayContains state and provides operations related to finding the next location a match could possibly begin. internal sealed class RegexFindOptimizations { - /// True if the input should be processed right-to-left rather than left-to-right. - private readonly bool _rightToLeft; /// Lookup table used for optimizing ASCII when doing set queries. private readonly uint[]?[]? _asciiLookups; - public RegexFindOptimizations(RegexNode root, RegexOptions options) + public static RegexFindOptimizations Create(RegexNode root, RegexOptions options) + { + RegexFindOptimizations opts = new(root, options, isLeadingPartial: false); + + if ((options & RegexOptions.RightToLeft) == 0 && + !opts.IsUseful && + RegexPrefixAnalyzer.FindLeadingPositiveLookahead(root) is RegexNode positiveLookahead) + { + RegexFindOptimizations positiveLookaheadOpts = new(positiveLookahead.Child(0), options, isLeadingPartial: true); + + // Fixups to incorporate relevant information from the original optimizations. + // - If the original has a larger minimum length than the lookahead, use it. Lookaheads don't currently factor into + // the computation of the minimum as it complicates the logic due to them possibly overlapping with other portions. + // - Use whatever max came from the original, if any. We shouldn't have computed a max for the lookahead because + // it's partial. + positiveLookaheadOpts.MinRequiredLength = Math.Max(opts.MinRequiredLength, positiveLookaheadOpts.MinRequiredLength); + positiveLookaheadOpts.MaxPossibleLength = opts.MaxPossibleLength; + + opts = positiveLookaheadOpts; + } + + return opts; + } + + /// Creates optimization information for searching with the pattern represented by . + /// The root of the pattern node tree. + /// Options used when creating the regex. + /// true if may not represent the whole pattern, only a leading node in it. + private RegexFindOptimizations(RegexNode root, RegexOptions options, bool isLeadingPartial) { - _rightToLeft = (options & RegexOptions.RightToLeft) != 0; + bool rightToLeft = (options & RegexOptions.RightToLeft) != 0; + Debug.Assert(!isLeadingPartial || !rightToLeft, "RightToLeft unexpected when isLeadingPartial"); MinRequiredLength = root.ComputeMinLength(); // Compute any anchor starting the expression. If there is one, we won't need to search for anything, // as we can just match at that single location. LeadingAnchor = RegexPrefixAnalyzer.FindLeadingAnchor(root); - if (_rightToLeft && LeadingAnchor == RegexNodeKind.Bol) + if (rightToLeft && LeadingAnchor == RegexNodeKind.Bol) { // Filter out Bol for RightToLeft, as we don't currently optimize for it. LeadingAnchor = RegexNodeKind.Unknown; } if (LeadingAnchor is RegexNodeKind.Beginning or RegexNodeKind.Start or RegexNodeKind.EndZ or RegexNodeKind.End) { - FindMode = (LeadingAnchor, _rightToLeft) switch + FindMode = (LeadingAnchor, rightToLeft) switch { (RegexNodeKind.Beginning, false) => FindNextStartingPositionMode.LeadingAnchor_LeftToRight_Beginning, (RegexNodeKind.Beginning, true) => FindNextStartingPositionMode.LeadingAnchor_RightToLeft_Beginning, @@ -47,7 +76,8 @@ public RegexFindOptimizations(RegexNode root, RegexOptions options) // Compute any anchor trailing the expression. If there is one, and we can also compute a fixed length // for the whole expression, we can use that to quickly jump to the right location in the input. - if (!_rightToLeft) // haven't added FindNextStartingPositionMode trailing anchor support for RTL + if (!rightToLeft && // haven't added FindNextStartingPositionMode trailing anchor support for RTL + !isLeadingPartial) // trailing anchors in a partial root aren't relevant { TrailingAnchor = RegexPrefixAnalyzer.FindTrailingAnchor(root); if (TrailingAnchor is RegexNodeKind.End or RegexNodeKind.EndZ && @@ -70,7 +100,7 @@ public RegexFindOptimizations(RegexNode root, RegexOptions options) if (prefix.Length > 1) { LeadingPrefix = prefix; - FindMode = _rightToLeft ? + FindMode = rightToLeft ? FindNextStartingPositionMode.LeadingString_RightToLeft : FindNextStartingPositionMode.LeadingString_LeftToRight; return; @@ -89,7 +119,7 @@ public RegexFindOptimizations(RegexNode root, RegexOptions options) // more expensive; someone who wants to pay to do more work can specify Compiled. So for the interpreter // we focus only on creating a set for the first character. Same for right-to-left, which is used very // rarely and thus we don't need to invest in special-casing it. - if (_rightToLeft) + if (rightToLeft) { // Determine a set for anything that can possibly start the expression. if (RegexPrefixAnalyzer.FindFirstCharClass(root) is string charClass) @@ -253,21 +283,21 @@ public RegexFindOptimizations(RegexNode root, RegexOptions options) public FindNextStartingPositionMode FindMode { get; } = FindNextStartingPositionMode.NoSearch; /// Gets the leading anchor (e.g. RegexNodeKind.Bol) if one exists and was computed. - public RegexNodeKind LeadingAnchor { get; } + public RegexNodeKind LeadingAnchor { get; private set; } /// Gets the trailing anchor (e.g. RegexNodeKind.Bol) if one exists and was computed. public RegexNodeKind TrailingAnchor { get; } /// Gets the minimum required length an input need be to match the pattern. /// 0 is a valid minimum length. This value may also be the max (and hence fixed) length of the expression. - public int MinRequiredLength { get; } + public int MinRequiredLength { get; private set; } /// The maximum possible length an input could be to match the pattern. /// /// This is currently only set when is found to be an end anchor. /// That can be expanded in the future as needed. /// - public int? MaxPossibleLength { get; } + public int? MaxPossibleLength { get; private set; } /// Gets the leading prefix. May be an empty string. public string LeadingPrefix { get; } = string.Empty; 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 8326cf4ef3eaca..706171f0ac599c 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 @@ -940,7 +940,7 @@ private RegexNode ReduceAlternation() node = ExtractCommonPrefixText(node); if (node.Kind == RegexNodeKind.Alternate) { - node = ExtractCommonPrefixOneNotoneSet(node); + node = ExtractCommonPrefixNode(node); if (node.Kind == RegexNodeKind.Alternate) { node = RemoveRedundantEmptiesAndNothings(node); @@ -1072,7 +1072,7 @@ void ReduceSingleLetterAndNestedAlternations() // This function optimizes out prefix nodes from alternation branches that are // the same across multiple contiguous branches. // e.g. \w12|\d34|\d56|\w78|\w90 => \w12|\d(?:34|56)|\w(?:78|90) - static RegexNode ExtractCommonPrefixOneNotoneSet(RegexNode alternation) + static RegexNode ExtractCommonPrefixNode(RegexNode alternation) { Debug.Assert(alternation.Kind == RegexNodeKind.Alternate); Debug.Assert(alternation.Children is List { Count: >= 2 }); @@ -1097,7 +1097,7 @@ static RegexNode ExtractCommonPrefixOneNotoneSet(RegexNode alternation) { Debug.Assert(children[startingIndex].Children is List { Count: >= 2 }); - // Only handle the case where each branch begins with the same One, Notone, or Set (individual or loop). + // Only handle the case where each branch begins with the same One, Notone, Set (individual or loop), or Anchor. // Note that while we can do this for individual characters, fixed length loops, and atomic loops, doing // it for non-atomic variable length loops could change behavior as each branch could otherwise have a // different number of characters consumed by the loop based on what's after it. @@ -1107,6 +1107,10 @@ static RegexNode ExtractCommonPrefixOneNotoneSet(RegexNode alternation) case RegexNodeKind.One or RegexNodeKind.Notone or RegexNodeKind.Set: case RegexNodeKind.Oneloopatomic or RegexNodeKind.Notoneloopatomic or RegexNodeKind.Setloopatomic: case RegexNodeKind.Oneloop or RegexNodeKind.Notoneloop or RegexNodeKind.Setloop or RegexNodeKind.Onelazy or RegexNodeKind.Notonelazy or RegexNodeKind.Setlazy when required.M == required.N: + case RegexNodeKind.Beginning or RegexNodeKind.Start or RegexNodeKind.Bol + or RegexNodeKind.End or RegexNodeKind.EndZ or RegexNodeKind.Eol + or RegexNodeKind.Boundary or RegexNodeKind.ECMABoundary + or RegexNodeKind.NonBoundary or RegexNodeKind.NonECMABoundary: break; default: diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexOpcode.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexOpcode.cs index d6b5998180a5fb..5f39d5b53a3bb2 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexOpcode.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexOpcode.cs @@ -117,54 +117,169 @@ internal enum RegexOpcode UpdateBumpalong = 46, // Primitive control structures - // TODO: Figure out what these comments mean / what these control structures actually do :) - /// back jump straight first. + /// Lazy branch in an alternation or conditional construct. + /// + /// On first execution, the opcode records the current input position (via the tracking stack) and continues straight + /// without taking the jump. When the matching that follows fails, backtracking will occur and the saved position is restored, + /// at which point the interpreter will jump to the alternative branch (using the patched jump offset in operand 0). + /// This opcode is used to implement alternation in a non-greedy (lazy) manner. + /// Lazybranch = 23, - /// back jump branch first for loop. + + /// Branch in a quantified loop that uses a saved mark to decide whether to repeat or exit. + /// + /// When executed, this opcode pops a previously saved input mark (from a or ) + /// and compares it to the current input position. If the loop's inner expression has consumed input (non-empty match), it + /// pushes updated state (saving the old mark and the current position) and jumps back (via the jump offset in operand 0) + /// to repeat the loop. If no progress has been made (empty match), it records state for backtracking and proceeds. + /// This opcode is used for greedy (non-lazy) quantified loops when no explicit counter is needed. + /// Branchmark = 24, - /// back jump straight first for loop. + + /// Lazy branch in a quantified loop that uses a saved mark. + /// + /// Similar in spirit to , this opcode is used for lazy loops. + /// It initially does not jump back to repeat the loop, preferring to let the overall match continue. + /// However, it saves the loop state so that if subsequent matching fails, backtracking will re-enter the loop body. + /// Special care is taken to handle empty matches so as to avoid infinite loops. + /// Lazybranchmark = 25, - /// back val set counter, null mark. + + /// Initialize the loop counter for a quantifier when the minimum repetition is zero. + /// + /// For quantified constructs with a minimum of zero ( == 0), this opcode pushes a counter + /// value (-1) along with a marker (implicitly indicating no match so far) onto the grouping stack. The operand (always 0 + /// in this case) is used in later comparisons within a or opcode. + /// Nullcount = 26, - /// back val set counter, make mark + + /// Initialize the loop counter for a quantifier with a positive minimum. + /// + /// When the quantifier requires at least one match (M > 0), this opcode pushes the current input position as a marker and a + /// counter value computed as (1 - M) onto the grouping stack. This counter will be adjusted in subsequent loop iterations + /// (via or ) to decide whether the loop should continue. + /// Setcount = 27, - /// back jump,limit branch++ if zero<=c<limit. + + /// Greedy counted branch for quantified loops. + /// + /// This opcode is used for quantified loops that require a counter. When executed, it pops the previously stored marker and counter + /// from the grouping stack, computes the difference between the current input position and the marker, and compares the counter + /// against a limit (given in operand 1). If the counter indicates that more iterations are allowed (and the inner expression consumed + /// input), it increments the counter, updates the marker with the new position, and jumps (via the jump offset in operand 0) to + /// repeat the loop. Otherwise, the interpreter continues straight. On backtracking, the previous state is restored so that a decreased + /// count may be tried. + /// Branchcount = 28, - /// back jump,limit same, but straight first. + + /// Lazy counted branch for quantified loops. + /// + /// This opcode is the lazy counterpart to . It is used in quantified loops that use a counter and prefer + /// to exit the loop as early as possible. On initial execution it will choose the straight path (i.e. not repeating the loop) if + /// the counter is nonnegative, but if the inner expression consumed input and the counter is below the maximum (given in operand 1), + /// it will re-enter the loop on backtracking. + /// Lazybranchcount = 29, - /// back save position. + + /// Push a null marker into the grouping stack for quantifiers with a minimum of zero when no explicit counter is needed. + /// + /// This opcode is similar to but is used in cases where the quantified construct does not require counting; + /// it pushes a marker value (-1) onto the grouping stack to record the starting position. On backtracking, the marker is simply removed. + /// Nullmark = 30, - /// back save position. + + /// Push the current input position onto the grouping stack. + /// + /// Used by grouping constructs (for capturing or to detect empty matches in loops), this opcode saves the current input position + /// so that later the interpreter can compare it to the current position to decide whether progress was made. It is the non-counting + /// counterpart to . + /// Setmark = 31, - /// back group define group. + + /// Completes a capturing group. + /// + /// When executed, this opcode pops a previously saved marker (the start position of the group) from the grouping stack and uses the + /// current input position as the end position. Operand 0 specifies the capture slot number. If operand 1 is not -1 then a prior capture + /// must have been made and a transfer of capture is performed. On backtracking, the capture is undone. + /// Capturemark = 32, - /// back recall position. + + /// Recall a previously saved marker. + /// + /// This opcode restores the input position from a marker saved on the grouping stack (typically via a or + /// ). It is used in lookaround constructs to revert the input position to the point where the lookaround began. + /// On backtracking, the marker is re-pushed onto the grouping stack. + /// Getmark = 33, - /// back save backtrack state. + + /// Mark the beginning of a non-backtracking / atomic region. + /// + /// This opcode is used at the start of constructs that must not be re-entered on backtracking (such as lookahead/lookbehind or atomic groups). + /// It saves the current backtracking state (including the current tracking and crawl positions) onto the grouping stack. + /// When the region is later exited (by ) the saved state is used to prevent further backtracking into the region. + /// Setjump = 34, - /// zap back to saved state. + + /// Restore state for a non-backtracking / atomic region on backtracking. + /// + /// Used in negative lookaround constructs, this opcode pops the saved backtracking and capture state (stored by a prior ) + /// and erases any changes made within the non-backtracking region. It thereby restores the state to what it was before entering the region. + /// Backjump = 35, - /// zap backtracking state. + + /// Finalize a non-backtracking / atomic region. + /// + /// This opcode is used at the end of lookaround or atomic group constructs to commit to the current matching path. + /// It pops the saved state from the grouping stack (stored by ), updates the tracking pointer (thereby + /// discarding any backtracking state from within the region), and then continues execution. On backtracking from such a region, + /// a variant of this opcode will undo any captures made. + /// Forejump = 36, - /// Backtrack if ref undefined. + + /// Test whether a particular backreference has already matched. + /// + /// Operand 0 is the capture group number to test. When executed, if the specified group has not captured any text, + /// the match fails and control transfers to backtracking. Otherwise, execution continues. This opcode is used in conditional + /// constructs where a branch is taken only if a given capture exists. + /// TestBackreference = 37, - /// jump just go. + + /// Unconditional jump. + /// + /// Operand 0 holds the target offset. When executed, the interpreter jumps unconditionally to that location. + /// This opcode is used to implement control flow for alternation and loop constructs. + /// Goto = 38, - /// done! + + /// Halt the interpreter. + /// + /// This opcode marks the end of the opcode stream. When reached, the matching process terminates and the result + /// (whether a match was found) is returned. + /// Stop = 40, // Modifiers for alternate modes /// Mask to get unmodified ordinary operator. OperatorMask = 63, + /// Indicates that we're reverse scanning. RightToLeft = 64, + /// Indicates that we're backtracking. Backtracking = 128, + /// Indicates that we're backtracking on a second branch. + /// + /// In patterns with alternations or complex quantifiers, multiple backtracking paths may be available. + /// This flag marks opcodes that are being processed on an alternate (or secondary) branch during backtracking, + /// as opposed to the primary branch. The interpreter uses this flag to apply specialized state restoration + /// or branch-selection logic when reverting from one branch to another. + /// BacktrackingSecond = 256, - /// Indicates that we're case-insensitive + + /// Indicates that we're case-insensitive. CaseInsensitive = 512, } } diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexPrefixAnalyzer.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexPrefixAnalyzer.cs index 556a187203f035..6be7fdda3fc948 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexPrefixAnalyzer.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexPrefixAnalyzer.cs @@ -1228,6 +1228,80 @@ public static (RegexNode LoopNode, (char Char, string? String, StringComparison return null; } + /// Finds a positive lookahead node that can be considered to start the pattern. + public static RegexNode? FindLeadingPositiveLookahead(RegexNode node) + { + RegexNode? positiveLookahead = null; + FindLeadingPositiveLookahead(node, ref positiveLookahead); + return positiveLookahead; + + // Returns whether to keep examining subsequent nodes in a concatenation. + static bool FindLeadingPositiveLookahead(RegexNode node, ref RegexNode? positiveLookahead) + { + if (!StackHelper.TryEnsureSufficientExecutionStack()) + { + return false; + } + + while (true) + { + if ((node.Options & RegexOptions.RightToLeft) != 0) + { + return false; + } + + switch (node.Kind) + { + case RegexNodeKind.PositiveLookaround: + // Got one. + positiveLookahead = node; + return false; + + case RegexNodeKind.Bol: + case RegexNodeKind.Eol: + case RegexNodeKind.Beginning: + case RegexNodeKind.Start: + case RegexNodeKind.EndZ: + case RegexNodeKind.End: + case RegexNodeKind.Boundary: + case RegexNodeKind.ECMABoundary: + case RegexNodeKind.NegativeLookaround: + case RegexNodeKind.Empty: + // Skip past zero-width nodes. + return true; + + case RegexNodeKind.Atomic: + case RegexNodeKind.Capture: + // Process the child instead of the group, which adds no semantics for this purpose. + node = node.Child(0); + continue; + + case RegexNodeKind.Loop or RegexNodeKind.Lazyloop when node.M >= 1: + // Process the child and then stop. We don't know how many times the + // loop will actually repeat, only that it must execute at least once. + FindLeadingPositiveLookahead(node.Child(0), ref positiveLookahead); + return false; + + case RegexNodeKind.Concatenate: + // Check each child, stopping the search if processing it says we can't process further. + int childCount = node.ChildCount(); + for (int i = 0; i < childCount; i++) + { + if (!FindLeadingPositiveLookahead(node.Child(i), ref positiveLookahead)) + { + return false; + } + } + + return true; + + default: + return false; + } + } + } + } + /// Computes the leading anchor of a node. public static RegexNodeKind FindLeadingAnchor(RegexNode node) => FindLeadingOrTrailingAnchor(node, leading: true); @@ -1263,7 +1337,14 @@ private static RegexNodeKind FindLeadingOrTrailingAnchor(RegexNode node, bool le case RegexNodeKind.Atomic: case RegexNodeKind.Capture: - // For groups, continue exploring the sole child. + case RegexNodeKind.Loop or RegexNodeKind.Lazyloop when leading && node.M >= 1: + case RegexNodeKind.PositiveLookaround when leading && (node.Options & RegexOptions.RightToLeft) == 0: + // For atomic and capture groups, for the purposes of finding anchors they add no semantics around the child. + // Loops are like atomic and captures for the purposes of finding leading anchors, as long as the loop has + // at least one guaranteed iteration (if its min is 0, any anchors inside might not apply). + // Positive lookaheads are also relevant as long as we're looking for leading anchors, as an anchor + // at the beginning of a starting positive lookahead has the same semantics as the same anchor at the + // beginning of the pattern. node = node.Child(0); continue; @@ -1274,15 +1355,35 @@ private static RegexNodeKind FindLeadingOrTrailingAnchor(RegexNode node, bool le { int childCount = node.ChildCount(); RegexNode? child = null; + RegexNodeKind bestAnchorFound = RegexNodeKind.Unknown; if (leading) { for (int i = 0; i < childCount; i++) { - if (node.Child(i).Kind is not (RegexNodeKind.Empty or RegexNodeKind.PositiveLookaround or RegexNodeKind.NegativeLookaround)) + RegexNode tmpChild = node.Child(i); + switch (tmpChild.Kind) { - child = node.Child(i); - break; + case RegexNodeKind.Empty or RegexNodeKind.NegativeLookaround: + case RegexNodeKind.PositiveLookaround when ((node.Options | tmpChild.Options) & RegexOptions.RightToLeft) != 0: + // Skip over zero-width assertions. + continue; + + case RegexNodeKind.PositiveLookaround: + // Except for positive lookaheads at the beginning of the pattern, as any anchor it has at + // its beginning can also be used. + bestAnchorFound = ChooseBetterAnchor(bestAnchorFound, FindLeadingOrTrailingAnchor(tmpChild, leading)); + if (IsBestAnchor(bestAnchorFound)) + { + return bestAnchorFound; + } + + // Now that we know what anchor might be in the lookahead, skip the zero-width assertion + // and continue examining subsequent nodes of the concatenation. + continue; } + + child = tmpChild; + break; } } else @@ -1297,13 +1398,57 @@ private static RegexNodeKind FindLeadingOrTrailingAnchor(RegexNode node, bool le } } - if (child is not null) + if (bestAnchorFound is not RegexNodeKind.Unknown) + { + // We found a leading anchor in a positive lookahead. If we don't have a child node, then + // just return the anchor we got. If we do have a child node, recur into it to find any anchor + // it has, and then choose the best between that and the lookahead. + Debug.Assert(leading); + return child is not null ? + ChooseBetterAnchor(bestAnchorFound, FindLeadingAnchor(child)) : + bestAnchorFound; + } + else if (child is not null) { + // Loop around to process the child node. node = child; continue; } goto default; + + // Decide which of two anchors we'd rather search for, based on which yields the fastest + // search, e.g. Beginning means we only have one place to search, whereas worse Bol could be at the + // start of any line, and even worse Boundary could be at the start or end of any word. + static RegexNodeKind ChooseBetterAnchor(RegexNodeKind anchor1, RegexNodeKind anchor2) + { + return + anchor1 == RegexNodeKind.Unknown ? anchor2 : + anchor2 == RegexNodeKind.Unknown ? anchor1 : + RankAnchorQuality(anchor1) >= RankAnchorQuality(anchor2) ? anchor1 : + anchor2; + + static int RankAnchorQuality(RegexNodeKind node) => + node switch + { + RegexNodeKind.Beginning => 3, + RegexNodeKind.Start => 3, + RegexNodeKind.End => 3, + RegexNodeKind.EndZ => 3, + + RegexNodeKind.Bol => 2, + RegexNodeKind.Eol => 2, + + RegexNodeKind.Boundary => 1, + RegexNodeKind.ECMABoundary => 1, + + _ => 0 + }; + } + + static bool IsBestAnchor(RegexNodeKind anchor) => + // Keep in sync with ChooseBetterAnchor + anchor is RegexNodeKind.Beginning or RegexNodeKind.Start or RegexNodeKind.End or RegexNodeKind.EndZ; } case RegexNodeKind.Alternate: diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexTree.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexTree.cs index 4ee944e47f91a5..8a827001cc2f98 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexTree.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexTree.cs @@ -77,7 +77,7 @@ internal RegexTree(RegexNode root, int captureCount, string[]? captureNames, Has CaptureNameToNumberMapping = captureNameToNumberMapping; CaptureNames = captureNames; Options = options; - FindOptimizations = new RegexFindOptimizations(root, options); + FindOptimizations = RegexFindOptimizations.Create(root, options); } } } diff --git a/src/libraries/System.Text.RegularExpressions/tests/UnitTests/RegexFindOptimizationsTests.cs b/src/libraries/System.Text.RegularExpressions/tests/UnitTests/RegexFindOptimizationsTests.cs index 4c0b02b266142e..c1030f86b11f1a 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/UnitTests/RegexFindOptimizationsTests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/UnitTests/RegexFindOptimizationsTests.cs @@ -30,6 +30,23 @@ public class RegexFindOptimizationsTests [InlineData(@"\zhello$", 0, (int)FindNextStartingPositionMode.LeadingAnchor_LeftToRight_End)] [InlineData(@"\zhi|\zhello", 0, (int)FindNextStartingPositionMode.LeadingAnchor_LeftToRight_End)] + [InlineData(@"(?=^abc)", 0, (int)FindNextStartingPositionMode.LeadingAnchor_LeftToRight_Beginning)] + [InlineData(@"(?=^)abc", 0, (int)FindNextStartingPositionMode.LeadingAnchor_LeftToRight_Beginning)] + [InlineData(@"(?=.*$)abc", 0, (int)FindNextStartingPositionMode.LeadingString_LeftToRight)] + [InlineData(@"(?=^)abc", (int)RegexOptions.RightToLeft, (int)FindNextStartingPositionMode.LeadingString_RightToLeft)] + [InlineData(@"abc(?=^)", (int)RegexOptions.RightToLeft, (int)FindNextStartingPositionMode.LeadingString_RightToLeft)] + [InlineData(@"(?[a-c]|def|[gh])")] [InlineData("this|that|there|then|those", "th(?>is|at|ere|en|ose)")] + [InlineData("^this|^that|^there|^then|^those", "^th(?>is|at|ere|en|ose)")] + [InlineData("\bthis|\bthat|\bthere|\bthen|\bthose", "\bth(?>is|at|ere|en|ose)")] [InlineData("it's (?>this|that|there|then|those)", "it's (?>th(?>is|at|e(?>re|n)|ose))")] [InlineData("it's (?>this|that|there|then|those)!", "it's (?>th(?>is|at|e(?>re|n)|ose))!")] [InlineData("abcd|abce", "abc[de]")] [InlineData("abcd|abef", "ab(?>cd|ef)")] [InlineData("abcd|aefg", "a(?>bcd|efg)")] [InlineData("abcd|abc|ab|a", "a(?>bcd|bc|b|)")] + [InlineData("^abcd|^abce", "^(?:abc[de])")] // [InlineData("abcde|abcdef", "abcde(?>|f)")] // TODO https://github.com/dotnet/runtime/issues/66031: Need to reorganize optimizations to avoid an extra Empty being left at the end of the tree [InlineData("abcdef|abcde", "abcde(?>f|)")] [InlineData("abcdef|abcdeg|abcdeh|abcdei|abcdej|abcdek|abcdel", "abcde[f-l]")] @@ -495,6 +498,8 @@ public void PatternsReduceIdentically(string actual, string expected) [InlineData("a*(?(xyz)acd|efg)", "(?>a*)(?(xyz)acd|efg)")] [InlineData("a*(?(xyz)bcd|afg)", "(?>a*)(?(xyz)bcd|afg)")] [InlineData("a*(?(xyz)bcd)", "(?>a*)(?(xyz)bcd)")] + // Different prefixes on alternation branches + [InlineData("^abcd|$abce", "^abcd|^abce")] public void PatternsReduceDifferently(string actual, string expected) { // NOTE: RegexNode.ToString is only compiled into debug builds, so DEBUG is currently set on the unit tests project. diff --git a/src/mono/msbuild/apple/build/AppleBuild.props b/src/mono/msbuild/apple/build/AppleBuild.props index c1e8952f835e76..a3fc8dc30b3be1 100644 --- a/src/mono/msbuild/apple/build/AppleBuild.props +++ b/src/mono/msbuild/apple/build/AppleBuild.props @@ -49,4 +49,4 @@ - \ No newline at end of file + diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index d3bf6132a19068..cd3ffe8533c5ed 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -569,7 +569,62 @@ int ISOSDacInterface.GetMethodDescName(ulong methodDesc, uint count, char* name, int ISOSDacInterface.GetMethodDescPtrFromFrame(ulong frameAddr, ulong* ppMD) => _legacyImpl is not null ? _legacyImpl.GetMethodDescPtrFromFrame(frameAddr, ppMD) : HResults.E_NOTIMPL; int ISOSDacInterface.GetMethodDescPtrFromIP(ulong ip, ulong* ppMD) - => _legacyImpl is not null ? _legacyImpl.GetMethodDescPtrFromIP(ip, ppMD) : HResults.E_NOTIMPL; + { + if (ip == 0 || ppMD == null) + return HResults.E_INVALIDARG; + + int hr = HResults.E_NOTIMPL; + + try + { + IExecutionManager executionManager = _target.Contracts.ExecutionManager; + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + + CodeBlockHandle? handle = executionManager.GetCodeBlockHandle(new TargetCodePointer(ip)); + if (handle is CodeBlockHandle codeHandle) + { + TargetPointer methodDescAddr = executionManager.GetMethodDesc(codeHandle); + + try + { + // Runs validation of MethodDesc + // if validation fails, should return E_INVALIDARG + rts.GetMethodDescHandle(methodDescAddr); + + *ppMD = methodDescAddr.Value; + hr = HResults.S_OK; + } + catch (System.Exception) + { + hr = HResults.E_INVALIDARG; + } + } + else + { + hr = HResults.E_FAIL; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + ulong ppMDLocal; + int hrLocal = _legacyImpl.GetMethodDescPtrFromIP(ip, &ppMDLocal); + + Debug.Assert(hrLocal == hr); + if (hr == HResults.S_OK) + { + Debug.Assert(*ppMD == ppMDLocal); + } + } +#endif + return hr; + } + int ISOSDacInterface.GetMethodDescTransparencyData(ulong methodDesc, void* data) => _legacyImpl is not null ? _legacyImpl.GetMethodDescTransparencyData(methodDesc, data) : HResults.E_NOTIMPL; int ISOSDacInterface.GetMethodTableData(ulong mt, DacpMethodTableData* data) diff --git a/src/tasks/AndroidAppBuilder/Templates/MainActivity.java b/src/tasks/AndroidAppBuilder/Templates/MainActivity.java index 360fda7973b3c5..7c81422f400c82 100644 --- a/src/tasks/AndroidAppBuilder/Templates/MainActivity.java +++ b/src/tasks/AndroidAppBuilder/Templates/MainActivity.java @@ -41,11 +41,13 @@ protected void onCreate(Bundle savedInstanceState) } final Activity ctx = this; + MonoRunner.initializeRuntime(entryPointLibName, ctx); new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { @Override public void run() { - int retcode = MonoRunner.initialize(entryPointLibName, new String[0], ctx); + int retcode = MonoRunner.executeEntryPoint(entryPointLibName, new String[0]); textView.setText("Mono Runtime returned: " + retcode); + ctx.reportFullyDrawn(); } }, 1000); } diff --git a/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java b/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java index 695d91068a261b..cb017daafcfdc1 100644 --- a/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java +++ b/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java @@ -74,7 +74,7 @@ public void onCreate(Bundle arguments) { start(); } - public static int initialize(String entryPointLibName, String[] args, Context context) { + public static void initializeRuntime(String entryPointLibName, Context context) { String filesDir = context.getFilesDir().getAbsolutePath(); String cacheDir = context.getCacheDir().getAbsolutePath(); @@ -90,9 +90,24 @@ public static int initialize(String entryPointLibName, String[] args, Context co // unzip libs and test files to filesDir unzipAssets(context, filesDir, "assets.zip"); - Log.i("DOTNET", "MonoRunner initialize,, entryPointLibName=" + entryPointLibName); + // set environment variables + setEnv("HOME", filesDir); + setEnv("TMPDIR", cacheDir); + setEnv("TEST_RESULTS_DIR", testResultsDir); + + Log.i("DOTNET", "MonoRunner initializeRuntime, entryPointLibName=" + entryPointLibName); int localDateTimeOffset = getLocalDateTimeOffset(); - return initRuntime(filesDir, cacheDir, testResultsDir, entryPointLibName, args, localDateTimeOffset); + int rv = initRuntime(filesDir, entryPointLibName, localDateTimeOffset); + if (rv != 0) { + Log.e("DOTNET", "Failed to initialize runtime, return-code=" + rv); + freeNativeResources(); + System.exit(rv); + } + } + + public static int executeEntryPoint(String entryPointLibName, String[] args) { + int rv = execEntryPoint(entryPointLibName, args); + return rv; } @Override @@ -104,7 +119,9 @@ public void onStart() { finish(1, null); return; } - int retcode = initialize(entryPointLibName, argsToForward, getContext()); + + initializeRuntime(entryPointLibName, getContext()); + int retcode = executeEntryPoint(entryPointLibName, argsToForward); Log.i("DOTNET", "MonoRunner finished, return-code=" + retcode); result.putInt("return-code", retcode); @@ -119,6 +136,14 @@ public void onStart() { finish(retcode, result); } + @Override + public void onDestroy() { + Log.i("DOTNET", "MonoRunner onDestroy"); + super.onDestroy(); + // Cleanup native resources + freeNativeResources(); + } + static void unzipAssets(Context context, String toPath, String zipName) { AssetManager assetManager = context.getAssets(); try { @@ -162,7 +187,11 @@ static int getLocalDateTimeOffset() { } } - static native int initRuntime(String libsDir, String cacheDir, String testResultsDir, String entryPointLibName, String[] args, int local_date_time_offset); - static native int setEnv(String key, String value); + + static native int initRuntime(String libsDir, String entryPointLibName, int local_date_time_offset); + + static native int execEntryPoint(String entryPointLibName, String[] args); + + static native void freeNativeResources(); } diff --git a/src/tasks/AndroidAppBuilder/Templates/monodroid-coreclr.c b/src/tasks/AndroidAppBuilder/Templates/monodroid-coreclr.c index b8fc9b27b59047..4489b0f23bfb83 100644 --- a/src/tasks/AndroidAppBuilder/Templates/monodroid-coreclr.c +++ b/src/tasks/AndroidAppBuilder/Templates/monodroid-coreclr.c @@ -24,12 +24,21 @@ void Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value); int -Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_testresults_dir, jstring j_entryPointLibName, jobjectArray j_args, long current_local_time); +Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_entryPointLibName, long current_local_time); + +int +Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args); + +void +Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz); /********* implementation *********/ -static char *bundle_path; -static char *executable; +static const char* g_bundle_path = NULL; +static const char* g_executable_path = NULL; +static unsigned int g_coreclr_domainId = 0; +static void* g_coreclr_handle = NULL; + #define LOG_INFO(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, "DOTNET", fmt, ##__VA_ARGS__) #define LOG_ERROR(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "DOTNET", fmt, ##__VA_ARGS__) @@ -54,21 +63,11 @@ strncpy_str (JNIEnv *env, char *buff, jstring str, int nbuff) jboolean isCopy = 0; const char *copy_buff = (*env)->GetStringUTFChars (env, str, &isCopy); strncpy (buff, copy_buff, nbuff); + buff[nbuff - 1] = '\0'; // ensure '\0' terminated if (isCopy) (*env)->ReleaseStringUTFChars (env, str, copy_buff); } -void -Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value) -{ - LOG_INFO ("Java_net_dot_MonoRunner_setEnv:"); - const char *key = (*env)->GetStringUTFChars(env, j_key, 0); - const char *val = (*env)->GetStringUTFChars(env, j_value, 0); - setenv (key, val, true); - (*env)->ReleaseStringUTFChars(env, j_key, key); - (*env)->ReleaseStringUTFChars(env, j_value, val); -} - /* * Get the list of trusted assemblies from a specified @dir_path. * The path is searched for .dll files which when found are concatenated @@ -130,11 +129,67 @@ get_tpas_from_path(const char* dir_path, const char** tpas) } static int -mono_droid_runtime_init (const char* executable, int managed_argc, char* managed_argv[], int local_date_time_offset) +bundle_executable_path (const char* executable, const char* bundle_path, const char** executable_path) { - LOG_INFO ("mono_droid_runtime_init called with executable: %s", executable); + size_t executable_path_len = strlen(bundle_path) + strlen(executable) + 1; // +1 for '/' + char* temp_path = (char*)malloc(sizeof(char) * (executable_path_len + 1)); // +1 for '\0' + if (temp_path == NULL) + { + return -1; + } - chdir (bundle_path); + size_t res = snprintf(temp_path, (executable_path_len + 1), "%s/%s", bundle_path, executable); + if (res < 0 || res != executable_path_len) + { + return -1; + } + *executable_path = temp_path; + return executable_path_len; +} + +static void +free_resources () +{ + if (g_bundle_path) + { + free (g_bundle_path); + g_bundle_path = NULL; + } + if (g_executable_path) + { + free (g_executable_path); + g_executable_path = NULL; + } + if (g_coreclr_handle) + { + // Clean up some coreclr resources. This doesn't make coreclr unloadable. + coreclr_shutdown (g_coreclr_handle, g_coreclr_domainId); + g_coreclr_handle = NULL; + } +} + +static int +mono_droid_execute_assembly (const char* executable_path, void* coreclr_handle, unsigned int coreclr_domainId, int managed_argc, const char** managed_argv) +{ + unsigned int rv; + LOG_INFO ("Calling coreclr_execute_assembly"); + coreclr_execute_assembly (coreclr_handle, coreclr_domainId, managed_argc, managed_argv, executable_path, &rv); + LOG_INFO ("Exit code: %u.", rv); + return rv; +} + +static int +mono_droid_runtime_init (const char* executable) +{ + LOG_INFO ("mono_droid_runtime_init (CoreCLR) called with executable: %s", executable); + + if (bundle_executable_path(executable, g_bundle_path, &g_executable_path) < 0) + { + LOG_ERROR("Failed to resolve full path for: %s", executable); + return -1; + } + + chdir (g_bundle_path); // TODO: set TRUSTED_PLATFORM_ASSEMBLIES, APP_PATHS and NATIVE_DLL_SEARCH_DIRECTORIES @@ -145,78 +200,99 @@ mono_droid_runtime_init (const char* executable, int managed_argc, char* managed const char* appctx_values[3]; appctx_values[0] = ANDROID_RUNTIME_IDENTIFIER; - appctx_values[1] = bundle_path; - size_t tpas_len = get_tpas_from_path(bundle_path, &appctx_values[2]); + appctx_values[1] = g_bundle_path; + size_t tpas_len = get_tpas_from_path(g_bundle_path, &appctx_values[2]); if (tpas_len < 1) { - LOG_ERROR("Failed to get trusted assemblies from path: %s", bundle_path); + LOG_ERROR("Failed to get trusted assemblies from path: %s", g_bundle_path); return -1; } - size_t executable_path_len = strlen(bundle_path) + strlen(executable) + 2; // +2 for '/' and '\0' - char* executable_path = (char*)malloc(executable_path_len); - size_t res = sprintf (executable_path, "%s/%s", bundle_path, executable); - if (res != executable_path_len - 1) - { - LOG_ERROR("Failed to resolve full path for: %s", executable); - return -1; - } - executable_path[res] = '\0'; - - unsigned int coreclr_domainId = 0; - void *coreclr_handle = NULL; - LOG_INFO ("Calling coreclr_initialize"); int rv = coreclr_initialize ( - executable_path, + g_executable_path, executable, 3, appctx_keys, appctx_values, - &coreclr_handle, - &coreclr_domainId + &g_coreclr_handle, + &g_coreclr_domainId ); LOG_INFO ("coreclr_initialize returned %d", rv); + return rv; +} - LOG_INFO ("Calling coreclr_execute_assembly"); - coreclr_execute_assembly (coreclr_handle, coreclr_domainId, managed_argc, managed_argv, executable_path, &rv); +void +Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value) +{ + LOG_INFO ("Java_net_dot_MonoRunner_setEnv:"); + assert (g_coreclr_handle == NULL); // setenv should be only called before the runtime is initialized - LOG_INFO ("Exit code: %d.", rv); - return rv; + const char *key = (*env)->GetStringUTFChars(env, j_key, 0); + const char *val = (*env)->GetStringUTFChars(env, j_value, 0); + + LOG_INFO ("Setting env: %s=%s", key, val); + setenv (key, val, true); + (*env)->ReleaseStringUTFChars(env, j_key, key); + (*env)->ReleaseStringUTFChars(env, j_value, val); } int -Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_testresults_dir, jstring j_entryPointLibName, jobjectArray j_args, long current_local_time) +Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_entryPointLibName, long current_local_time) { - LOG_INFO ("Java_net_dot_MonoRunner_initRuntime:"); + LOG_INFO ("Java_net_dot_MonoRunner_initRuntime (CoreCLR):"); char file_dir[2048]; - char cache_dir[2048]; - char testresults_dir[2048]; char entryPointLibName[2048]; strncpy_str (env, file_dir, j_files_dir, sizeof(file_dir)); - strncpy_str (env, cache_dir, j_cache_dir, sizeof(cache_dir)); - strncpy_str (env, testresults_dir, j_testresults_dir, sizeof(testresults_dir)); strncpy_str (env, entryPointLibName, j_entryPointLibName, sizeof(entryPointLibName)); - bundle_path = file_dir; - executable = entryPointLibName; + size_t file_dir_len = strlen(file_dir); + char* bundle_path_tmp = (char*)malloc(sizeof(char) * (file_dir_len + 1)); // +1 for '\0' + if (bundle_path_tmp == NULL) + { + LOG_ERROR("Failed to allocate memory for bundle_path"); + return -1; + } + strncpy(bundle_path_tmp, file_dir, file_dir_len + 1); + g_bundle_path = bundle_path_tmp; + + return mono_droid_runtime_init (entryPointLibName); +} + +int +Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args) +{ + LOG_INFO("Java_net_dot_MonoRunner_execEntryPoint (CoreCLR):"); + + if ((g_bundle_path == NULL) || (g_executable_path == NULL)) + { + LOG_ERROR("Bundle path or executable path not set"); + return -1; + } - setenv ("HOME", bundle_path, true); - setenv ("TMPDIR", cache_dir, true); - setenv ("TEST_RESULTS_DIR", testresults_dir, true); + if ((g_coreclr_handle == NULL) || (g_coreclr_domainId == 0)) + { + LOG_ERROR("CoreCLR not initialized"); + return -1; + } - int args_len = (*env)->GetArrayLength(env, j_args); + int args_len = (*env)->GetArrayLength (env, j_args); int managed_argc = args_len + 1; - char** managed_argv = (char**)malloc(managed_argc * sizeof(char*)); + const char** managed_argv = (const char**)malloc (managed_argc * sizeof(char*)); + if (managed_argv == NULL) + { + LOG_ERROR("Failed to allocate memory for managed_argv"); + return -1; + } - managed_argv[0] = bundle_path; + managed_argv[0] = g_bundle_path; for (int i = 0; i < args_len; ++i) { jstring j_arg = (*env)->GetObjectArrayElement(env, j_args, i); managed_argv[i + 1] = (char*)((*env)->GetStringUTFChars(env, j_arg, NULL)); } - int res = mono_droid_runtime_init (executable, managed_argc, managed_argv, current_local_time); + int rv = mono_droid_execute_assembly (g_executable_path, g_coreclr_handle, g_coreclr_domainId, managed_argc, managed_argv); for (int i = 0; i < args_len; ++i) { @@ -225,6 +301,12 @@ Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_ } free(managed_argv); - return res; + return rv; } +void +Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz) +{ + LOG_INFO ("Java_net_dot_MonoRunner_freeNativeResources (CoreCLR):"); + free_resources (); +} diff --git a/src/tasks/AndroidAppBuilder/Templates/monodroid-librarymode.c b/src/tasks/AndroidAppBuilder/Templates/monodroid-librarymode.c index 2ab922255053a1..ef2f7f6a0744ca 100644 --- a/src/tasks/AndroidAppBuilder/Templates/monodroid-librarymode.c +++ b/src/tasks/AndroidAppBuilder/Templates/monodroid-librarymode.c @@ -15,7 +15,13 @@ void Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value); int -Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_testresults_dir, jstring j_entryPointLibName, jobjectArray j_args, long current_local_time); +Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_entryPointLibName, long current_local_time); + +int +Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args); + +void +Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz); /********* imported symbols *********/ void SayHello (); @@ -28,6 +34,7 @@ strncpy_str (JNIEnv *env, char *buff, jstring str, int nbuff) jboolean isCopy = 0; const char *copy_buff = (*env)->GetStringUTFChars (env, str, &isCopy); strncpy (buff, copy_buff, nbuff); + buff[nbuff - 1] = '\0'; // ensure '\0' terminated if (isCopy) (*env)->ReleaseStringUTFChars (env, str, copy_buff); } @@ -51,22 +58,26 @@ Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstrin } int -Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_testresults_dir, jstring j_entryPointLibName, jobjectArray j_args, long current_local_time) +Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_entryPointLibName, long current_local_time) { char file_dir[2048]; - char cache_dir[2048]; - char testresults_dir[2048]; strncpy_str (env, file_dir, j_files_dir, sizeof(file_dir)); - strncpy_str (env, cache_dir, j_cache_dir, sizeof(cache_dir)); - strncpy_str (env, testresults_dir, j_testresults_dir, sizeof(testresults_dir)); - setenv ("HOME", file_dir, true); setenv ("DOTNET_LIBRARY_ASSEMBLY_PATH", file_dir, true); - setenv ("TMPDIR", cache_dir, true); - setenv ("TEST_RESULTS_DIR", testresults_dir, true); //setenv ("MONO_LOG_LEVEL", "debug", true); //setenv ("MONO_LOG_MASK", "all", true); + return 0; +} +int +Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args) +{ return invoke_netlibrary_entrypoints (); } + +void +Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz) +{ + // nothing to do +} diff --git a/src/tasks/AndroidAppBuilder/Templates/monodroid.c b/src/tasks/AndroidAppBuilder/Templates/monodroid.c index d139109f7b0a90..5ba3952a43d78a 100644 --- a/src/tasks/AndroidAppBuilder/Templates/monodroid.c +++ b/src/tasks/AndroidAppBuilder/Templates/monodroid.c @@ -34,7 +34,13 @@ void Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value); int -Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_testresults_dir, jstring j_entryPointLibName, jobjectArray j_args, long current_local_time); +Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_entryPointLibName, long current_local_time); + +int +Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args); + +void +Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz); // called from C# void @@ -42,8 +48,9 @@ invoke_external_native_api (void (*callback)(void)); /********* implementation *********/ -static char *bundle_path; -static char *executable; +static const char* g_bundle_path = NULL; +static MonoDomain* g_domain = NULL; +static MonoAssembly* g_assembly = NULL; #define LOG_INFO(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, "DOTNET", fmt, ##__VA_ARGS__) #define LOG_ERROR(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "DOTNET", fmt, ##__VA_ARGS__) @@ -65,11 +72,12 @@ static char *executable; static MonoAssembly* mono_droid_load_assembly (const char *name, const char *culture) { + assert (g_bundle_path); char filename [1024]; char path [1024]; int res; - LOG_INFO ("assembly_preload_hook: %s %s %s\n", name, culture, bundle_path); + LOG_INFO ("assembly_preload_hook: %s %s %s\n", name, culture, g_bundle_path); int len = strlen (name); int has_extension = len > 3 && name [len - 4] == '.' && (!strcmp ("exe", name + (len - 3)) || !strcmp ("dll", name + (len - 3))); @@ -81,9 +89,9 @@ mono_droid_load_assembly (const char *name, const char *culture) } if (culture && strcmp (culture, "")) - res = snprintf (path, sizeof (path) - 1, "%s/%s/%s", bundle_path, culture, filename); + res = snprintf (path, sizeof (path) - 1, "%s/%s/%s", g_bundle_path, culture, filename); else - res = snprintf (path, sizeof (path) - 1, "%s/%s", bundle_path, filename); + res = snprintf (path, sizeof (path) - 1, "%s/%s", g_bundle_path, filename); assert (res > 0); struct stat buffer; @@ -106,6 +114,7 @@ mono_droid_assembly_preload_hook (MonoAssemblyName *aname, char **assemblies_pat static unsigned char * load_aot_data (MonoAssembly *assembly, int size, void *user_data, void **out_handle) { + assert (g_bundle_path); *out_handle = NULL; char path [1024]; @@ -115,7 +124,7 @@ load_aot_data (MonoAssembly *assembly, int size, void *user_data, void **out_han const char *aname = mono_assembly_name_get_name (assembly_name); LOG_INFO ("Looking for aot data for assembly '%s'.", aname); - res = snprintf (path, sizeof (path) - 1, "%s/%s.aotdata", bundle_path, aname); + res = snprintf (path, sizeof (path) - 1, "%s/%s.aotdata", g_bundle_path, aname); assert (res > 0); int fd = open (path, O_RDONLY); @@ -222,8 +231,9 @@ cleanup_runtime_config (MonovmRuntimeConfigArguments *args, void *user_data) } static int -mono_droid_runtime_init (const char* executable, int managed_argc, char* managed_argv[], int local_date_time_offset) +mono_droid_runtime_init (const char* executable, int local_date_time_offset) { + LOG_INFO ("mono_droid_runtime_init (Mono) called with executable: %s", executable); // NOTE: these options can be set via command line args for adb or xharness, see AndroidSampleApp.csproj // uncomment for debug output: @@ -240,7 +250,7 @@ mono_droid_runtime_init (const char* executable, int managed_argc, char* managed #endif bool wait_for_debugger = false; - chdir (bundle_path); + chdir (g_bundle_path); // TODO: set TRUSTED_PLATFORM_ASSEMBLIES, APP_PATHS and NATIVE_DLL_SEARCH_DIRECTORIES @@ -251,15 +261,15 @@ mono_droid_runtime_init (const char* executable, int managed_argc, char* managed const char* appctx_values[3]; appctx_values[0] = ANDROID_RUNTIME_IDENTIFIER; - appctx_values[1] = bundle_path; + appctx_values[1] = g_bundle_path; char local_date_time_offset_buffer[32]; snprintf (local_date_time_offset_buffer, sizeof(local_date_time_offset_buffer), "%d", local_date_time_offset); appctx_values[2] = strdup (local_date_time_offset_buffer); char *file_name = RUNTIMECONFIG_BIN_FILE; - int str_len = strlen (bundle_path) + strlen (file_name) + 1; // +1 is for the "/" + int str_len = strlen (g_bundle_path) + strlen (file_name) + 1; // +1 is for the "/" char *file_path = (char *)malloc (sizeof (char) * (str_len +1)); // +1 is for the terminating null character - int num_char = snprintf (file_path, (str_len + 1), "%s/%s", bundle_path, file_name); + int num_char = snprintf (file_path, (str_len + 1), "%s/%s", g_bundle_path, file_name); struct stat buffer; LOG_INFO ("file_path: %s\n", file_path); @@ -274,7 +284,9 @@ mono_droid_runtime_init (const char* executable, int managed_argc, char* managed free (file_path); } - monovm_initialize(3, appctx_keys, appctx_values); + LOG_INFO ("Calling monovm_initialize"); + int rv = monovm_initialize(3, appctx_keys, appctx_values); + LOG_INFO ("monovm_initialize returned %d", rv); mono_debug_init (MONO_DEBUG_FORMAT_MONO); mono_install_assembly_preload_hook (mono_droid_assembly_preload_hook, NULL); @@ -304,20 +316,54 @@ mono_droid_runtime_init (const char* executable, int managed_argc, char* managed mono_jit_set_aot_mode(MONO_AOT_MODE_NORMAL); #endif // FULL_AOT #endif // FORCE_INTERPRETER + + g_domain = mono_jit_init_version ("dotnet.android", "mobile"); + if (g_domain == NULL) { + LOG_ERROR ("mono_jit_init_version failed"); + return -1; + } - MonoDomain *domain = mono_jit_init_version ("dotnet.android", "mobile"); - assert (domain); + g_assembly = mono_droid_load_assembly (executable, NULL); + if (g_assembly == NULL) { + LOG_ERROR ("mono_droid_load_assembly failed"); + return -1; + } + + return rv; +} - MonoAssembly *assembly = mono_droid_load_assembly (executable, NULL); - assert (assembly); +static void +free_resources () +{ + if (g_bundle_path) + { + free (g_bundle_path); + g_bundle_path = NULL; + } + if (g_assembly) + { + mono_assembly_close (g_assembly); + g_assembly = NULL; + } + // Free g_domain + if (g_domain) + { + mono_domain_set (g_domain, false); + mono_domain_finalize (g_domain, 0); + g_domain = NULL; + } +} - LOG_INFO ("Executable: %s", executable); - int res = mono_jit_exec (domain, assembly, managed_argc, managed_argv); - LOG_INFO ("Exit code: %d.", res); +static int +mono_droid_execute_assembly (MonoDomain* domain, MonoAssembly* assembly, int managed_argc, char* managed_argv[]) +{ + LOG_INFO ("Calling mono_jit_exec"); + int rv = mono_jit_exec (domain, assembly, managed_argc, managed_argv); + LOG_INFO ("Exit code: %d.", rv); mono_jit_cleanup (domain); - return res; + return rv; } static void @@ -326,6 +372,7 @@ strncpy_str (JNIEnv *env, char *buff, jstring str, int nbuff) jboolean isCopy = 0; const char *copy_buff = (*env)->GetStringUTFChars (env, str, &isCopy); strncpy (buff, copy_buff, nbuff); + buff[nbuff - 1] = '\0'; // ensure '\0' terminated if (isCopy) (*env)->ReleaseStringUTFChars (env, str, copy_buff); } @@ -333,44 +380,68 @@ strncpy_str (JNIEnv *env, char *buff, jstring str, int nbuff) void Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value) { + LOG_INFO ("Java_net_dot_MonoRunner_setEnv:"); + assert (g_domain == NULL); // setenv should be only called before the runtime is initialized + const char *key = (*env)->GetStringUTFChars(env, j_key, 0); const char *val = (*env)->GetStringUTFChars(env, j_value, 0); + + LOG_INFO ("Setting env var: %s=%s", key, val); setenv (key, val, true); (*env)->ReleaseStringUTFChars(env, j_key, key); (*env)->ReleaseStringUTFChars(env, j_value, val); } int -Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_testresults_dir, jstring j_entryPointLibName, jobjectArray j_args, long current_local_time) +Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_entryPointLibName, long current_local_time) { + LOG_INFO ("Java_net_dot_MonoRunner_initRuntime (Mono):"); char file_dir[2048]; - char cache_dir[2048]; - char testresults_dir[2048]; char entryPointLibName[2048]; strncpy_str (env, file_dir, j_files_dir, sizeof(file_dir)); - strncpy_str (env, cache_dir, j_cache_dir, sizeof(cache_dir)); - strncpy_str (env, testresults_dir, j_testresults_dir, sizeof(testresults_dir)); strncpy_str (env, entryPointLibName, j_entryPointLibName, sizeof(entryPointLibName)); - bundle_path = file_dir; - executable = entryPointLibName; + size_t file_dir_len = strlen(file_dir); + char* bundle_path_tmp = (char*)malloc(sizeof(char) * (file_dir_len + 1)); // +1 for '\0' + if (bundle_path_tmp == NULL) + { + LOG_ERROR("Failed to allocate memory for bundle_path"); + return -1; + } + strncpy(bundle_path_tmp, file_dir, file_dir_len + 1); + g_bundle_path = bundle_path_tmp; + + return mono_droid_runtime_init (entryPointLibName, current_local_time); +} + +int +Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args) +{ + LOG_INFO("Java_net_dot_MonoRunner_execEntryPoint (Mono):"); + + if (g_bundle_path == NULL) + { + LOG_ERROR("Bundle path or executable name not set"); + return -1; + } - setenv ("HOME", bundle_path, true); - setenv ("TMPDIR", cache_dir, true); - setenv ("TEST_RESULTS_DIR", testresults_dir, true); + if (g_domain == NULL || g_assembly == NULL) + { + LOG_ERROR("Mono domain or assembly not initialized"); + return -1; + } int args_len = (*env)->GetArrayLength(env, j_args); int managed_argc = args_len + 1; char** managed_argv = (char**)malloc(managed_argc * sizeof(char*)); - - managed_argv[0] = bundle_path; + managed_argv[0] = g_bundle_path; for (int i = 0; i < args_len; ++i) { jstring j_arg = (*env)->GetObjectArrayElement(env, j_args, i); managed_argv[i + 1] = (char*)((*env)->GetStringUTFChars(env, j_arg, NULL)); } - int res = mono_droid_runtime_init (executable, managed_argc, managed_argv, current_local_time); + int rv = mono_droid_execute_assembly (g_domain, g_assembly, managed_argc, managed_argv); for (int i = 0; i < args_len; ++i) { @@ -379,7 +450,14 @@ Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_ } free(managed_argv); - return res; + return rv; +} + +void +Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz) +{ + LOG_INFO ("Java_net_dot_MonoRunner_freeNativeResources (Mono):"); + free_resources (); } // called from C# diff --git a/src/tasks/MonoTargetsTasks/ILStrip/ILStrip.cs b/src/tasks/MonoTargetsTasks/ILStrip/ILStrip.cs index a71e2b69321f7d..288c7ef3711c8a 100644 --- a/src/tasks/MonoTargetsTasks/ILStrip/ILStrip.cs +++ b/src/tasks/MonoTargetsTasks/ILStrip/ILStrip.cs @@ -15,7 +15,6 @@ using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; -using System.Buffers; using System.Collections.Concurrent; public class ILStrip : Microsoft.Build.Utilities.Task @@ -305,32 +304,27 @@ private Dictionary ComputeMethodBodyUsage(MetadataReader mr, StreamRea private void CreateTrimmedAssembly(PEReader peReader, string trimmedAssemblyFilePath, FileStream fs, Dictionary methodBodyUses) { using FileStream os = File.Open(trimmedAssemblyFilePath, FileMode.Create); - { - fs.Position = 0; - MemoryStream memStream = new MemoryStream((int)fs.Length); - fs.CopyTo(memStream); - foreach (var kvp in methodBodyUses) + fs.Position = 0; + fs.CopyTo(os); + + foreach (var kvp in methodBodyUses) + { + int rva = kvp.Key; + int count = kvp.Value; + if (count == 0) { - int rva = kvp.Key; - int count = kvp.Value; - if (count == 0) - { - int methodSize = ComputeMethodSize(peReader, rva); - int actualLoc = ComputeMethodHash(peReader, rva); - int headerSize = ComputeMethodHeaderSize(memStream, actualLoc); - if (headerSize == 1) //Set code size to zero for TinyFormat - SetCodeSizeToZeroForTiny(ref memStream, actualLoc); - ZeroOutMethodBody(ref memStream, methodSize, actualLoc, headerSize); - } - else if (count < 0) - { - Log.LogError($"Method usage count is less than zero for rva: {rva}."); - } + int methodSize = ComputeMethodSize(peReader, rva); + int actualLoc = ComputeMethodHash(peReader, rva); + int headerSize = ComputeMethodHeaderSize(fs, actualLoc); + if (headerSize == 1) //Set code size to zero for TinyFormat + SetCodeSizeToZeroForTiny(os, actualLoc); + ZeroOutMethodBody(os, methodSize, actualLoc, headerSize); + } + else if (count < 0) + { + Log.LogError($"Method usage count is less than zero for rva: {rva}."); } - - memStream.Position = 0; - memStream.CopyTo(os); } } @@ -343,30 +337,27 @@ private static int ComputeMethodHash(PEReader peReader, int rva) return (peReader.PEHeaders.SectionHeaders[sectionIndex].PointerToRawData + relativeOffset); } - private static int ComputeMethodHeaderSize(MemoryStream memStream, int actualLoc) + private static int ComputeMethodHeaderSize(Stream stream, int actualLoc) { - memStream.Position = actualLoc; - int firstbyte = memStream.ReadByte(); + stream.Position = actualLoc; + int firstbyte = stream.ReadByte(); int headerFlag = firstbyte & 0b11; return (headerFlag == 2 ? 1 : 4); } - private static void SetCodeSizeToZeroForTiny(ref MemoryStream memStream, int actualLoc) + private static void SetCodeSizeToZeroForTiny(Stream stream, int actualLoc) { - memStream.Position = actualLoc; - byte[] header = {0b10}; - memStream.Write(header, 0, 1); + stream.Position = actualLoc; + stream.WriteByte(0b10); } - private static void ZeroOutMethodBody(ref MemoryStream memStream, int methodSize, int actualLoc, int headerSize) + private static void ZeroOutMethodBody(Stream stream, int methodSize, int actualLoc, int headerSize) { - memStream.Position = actualLoc + headerSize; - - byte[] zeroBuffer; - zeroBuffer = ArrayPool.Shared.Rent(methodSize); - Array.Clear(zeroBuffer, 0, zeroBuffer.Length); - memStream.Write(zeroBuffer, 0, methodSize - headerSize); - ArrayPool.Shared.Return(zeroBuffer); + stream.Position = actualLoc + headerSize; + for (int i = 0; i < methodSize - headerSize; i++) + { + stream.WriteByte(0); + } } private static TaskItem GetTrimmedAssemblyItem(ITaskItem assemblyItem, string trimmedAssemblyFilePath, string originAssemblyFilePath) diff --git a/src/tests/Common/CoreCLRTestLibrary/PlatformDetection.cs b/src/tests/Common/CoreCLRTestLibrary/PlatformDetection.cs index 0a47a6a8ee9709..f07d33fe94cfa6 100644 --- a/src/tests/Common/CoreCLRTestLibrary/PlatformDetection.cs +++ b/src/tests/Common/CoreCLRTestLibrary/PlatformDetection.cs @@ -64,5 +64,6 @@ public static bool IsNonZeroLowerBoundArraySupported // These platforms have not had their infrastructure updated to support native test assets. public static bool PlatformDoesNotSupportNativeTestAssets => OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsWatchOS() || OperatingSystem.IsAndroid() || OperatingSystem.IsBrowser() || OperatingSystem.IsWasi(); + public static bool IsAppleMobile => OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsWatchOS() || OperatingSystem.IsMacCatalyst(); } } diff --git a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs index 9e7b2e0104df08..6f0e669dda18a3 100644 --- a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs +++ b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs @@ -259,7 +259,7 @@ private static void AddRunnerSource(SourceProductionContext context, ImmutableAr { if (targetOS?.ToLowerInvariant() is "ios" or "iossimulator" or "tvos" or "tvossimulator" or "maccatalyst" or "android" or "browser") { - context.AddSource("XHarnessRunner.g.cs", GenerateXHarnessTestRunner(methods, aliasMap, assemblyName)); + context.AddSource("XHarnessRunner.g.cs", GenerateXHarnessTestRunner(methods, aliasMap, assemblyName, targetOS)); } else { @@ -431,7 +431,7 @@ private static string GenerateFullTestRunner(ImmutableArray testInfos return builder.GetCode(); } - private static string GenerateXHarnessTestRunner(ImmutableArray testInfos, ImmutableDictionary aliasMap, string assemblyName) + private static string GenerateXHarnessTestRunner(ImmutableArray testInfos, ImmutableDictionary aliasMap, string assemblyName, string? targetOS) { // For simplicity, we'll use top-level statements for the generated Main method. CodeBuilder builder = new(); @@ -440,6 +440,18 @@ private static string GenerateXHarnessTestRunner(ImmutableArray testI builder.AppendLine("XUnitWrapperLibrary.TestSummary summary;"); builder.AppendLine("System.Diagnostics.Stopwatch stopwatch;"); builder.AppendLine("XUnitWrapperLibrary.TestOutputRecorder outputRecorder;"); + if (targetOS?.ToLowerInvariant() is "ios" or "iossimulator" or "tvos" or "tvossimulator" or "maccatalyst") + { + builder.AppendLine("string documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);"); + builder.AppendLine($@"string tempLogPath = System.IO.Path.Combine(documentsPath, ""{assemblyName}.templog.xml"");"); + builder.AppendLine($@"string testStatsPath = System.IO.Path.Combine(documentsPath, ""{assemblyName}.testStats.csv"");"); + } + else + { + builder.AppendLine($@"string tempLogPath = ""{assemblyName}.templog.xml"";"); + builder.AppendLine($@"string testStatsPath = ""{assemblyName}.testStats.csv"";"); + } + builder.AppendLine(); builder.AppendLine("try"); @@ -468,16 +480,16 @@ private static string GenerateXHarnessTestRunner(ImmutableArray testI using (builder.NewBracesScope()) { - builder.AppendLine($@"if (System.IO.File.Exists(""{assemblyName}.tempLog.xml""))"); + builder.AppendLine("if (System.IO.File.Exists(tempLogPath))"); using (builder.NewBracesScope()) { - builder.AppendLine($@"System.IO.File.Delete(""{assemblyName}.tempLog.xml"");"); + builder.AppendLine("System.IO.File.Delete(tempLogPath);"); } - builder.AppendLine($@"if (System.IO.File.Exists(""{assemblyName}.testStats.csv""))"); + builder.AppendLine("if (System.IO.File.Exists(testStatsPath))"); using (builder.NewBracesScope()) { - builder.AppendLine($@"System.IO.File.Delete(""{assemblyName}.testStats.csv"");"); + builder.AppendLine("System.IO.File.Delete(testStatsPath);"); } builder.AppendLine(); @@ -495,8 +507,8 @@ private static string GenerateXHarnessTestRunner(ImmutableArray testI builder.AppendLine("Initialize();"); // Open the stream writer for the temp log. - builder.AppendLine($@"using (System.IO.StreamWriter tempLogSw = System.IO.File.AppendText(""{assemblyName}.templog.xml""))"); - builder.AppendLine($@"using (System.IO.StreamWriter statsCsvSw = System.IO.File.AppendText(""{assemblyName}.testStats.csv""))"); + builder.AppendLine($"using (System.IO.StreamWriter tempLogSw = System.IO.File.AppendText(tempLogPath))"); + builder.AppendLine($"using (System.IO.StreamWriter statsCsvSw = System.IO.File.AppendText(testStatsPath))"); CodeBuilder testExecutorBuilder = new(); using (builder.NewBracesScope()) diff --git a/src/tests/Common/helixpublishwitharcade.proj b/src/tests/Common/helixpublishwitharcade.proj index 21238d37183a89..e6f3193a7490e8 100644 --- a/src/tests/Common/helixpublishwitharcade.proj +++ b/src/tests/Common/helixpublishwitharcade.proj @@ -331,13 +331,19 @@ + + + <_AppBundleRunScriptName>RunTests + <_AppBundleRunScriptName Condition="'$(TargetsAppleMobile)' == 'true' and '$(NeedsToBuildAppsOnHelix)' == 'true'">build-apple-app + + <_MergedWrapperMarker Include="$(TestBinDir)**\*.MergedTestAssembly" Exclude="$(TestBinDir)**\supportFiles\*.MergedTestAssembly" /> <_MergedWrapperMarker Update="@(_MergedWrapperMarker)"> $([System.IO.Path]::ChangeExtension('%(Identity)', '.$(TestScriptExtension)')) - %(RootDir)%(Directory)AppBundle/RunTests.$(TestScriptExtension) + %(RootDir)%(Directory)AppBundle/$(_AppBundleRunScriptName).$(TestScriptExtension) @@ -537,7 +543,7 @@ @@ -574,9 +580,45 @@ - + + + + + + <_MergedWrapperDirectory>%(_MergedWrapperMarker.RootDir)%(Directory) + <_MergedWrapperName>%(_MergedWrapperMarker.FileName) + + + + <_MergedPayloadGroups Include="$(_MergedWrapperName)" /> + <_MergedPayloadFiles Include="$(_MergedWrapperDirectory)AppBundle/**" /> + <_MergedPayloadFiles Update="@(_MergedPayloadFiles)" Condition="'@(_MergedPayloadFiles)' != ''" > + + $([System.IO.Path]::GetRelativePath('$(_MergedWrapperDirectory)AppBundle/', %(FullPath))) + + + <_TestExclusionListPlaceholder Include="@(_MergedPayloadFiles)" Condition="$([System.String]::new('%(FileName)').EndsWith('TestExclusionList'))" /> + <_MergedPayloadFiles Remove="@(_TestExclusionListPlaceholder)" /> + + + + + + - + @@ -593,7 +635,9 @@ DestinationFile="$(LegacyPayloadsRootDirectory)\%(LegacyPayloads.PayloadGroup).zip" /> - + @@ -864,6 +908,61 @@ + + + + $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'msbuild', 'apple', 'build')) + $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'msbuild', 'common')) + https://netcorenativeassets.blob.core.windows.net/resource-packages/external/macos/cmake/cmake-3.28.0-macos-universal.tar.gz + + <_XHarnessAppleCustomCommand> + source build-apple-app.sh + + <_RuntimeComponentManifestDir>$([MSBuild]::NormalizeDirectory('$(MonoArtifactsPath)', 'build')) + + + + + + + + + + + + + + + + + + + + + + <_XHarnessAppBundleZipWorkItems Include="@(XHarnessAppBundleToTest->'$(MergedPayloadsRootDirectory)%(PayloadGroup).zip')" RemoveMetadata="AppBundlePath"> + $(_XHarnessAppleCustomCommand) + + + + + + + $(HelixPreCommands);codesign -s - -f --preserve-metadata=entitlements $HELIX_CORRELATION_PAYLOAD/createdump diff --git a/src/tests/Common/mergedrunnermobile.targets b/src/tests/Common/mergedrunnermobile.targets index 7371cdc702c5a0..187120666f49f8 100644 --- a/src/tests/Common/mergedrunnermobile.targets +++ b/src/tests/Common/mergedrunnermobile.targets @@ -5,6 +5,8 @@ $(AssemblyName).dll $(AssemblyName).dll GeneratedRunner + + false @@ -44,4 +46,4 @@ - \ No newline at end of file + diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets index 85404d96386aee..ab104f7a4deedd 100644 --- a/src/tests/Directory.Build.targets +++ b/src/tests/Directory.Build.targets @@ -9,29 +9,6 @@ - - $(TargetOS).AnyCPU.$(Configuration) - $(ArtifactsDir)helix/ - $(TestArchiveRoot)tests/ - $(TestArchiveTestsRoot)$(OSPlatformConfig)/ - $(TestArchiveRoot)runtime/ - - BuildMonoiOSApp - BuildMonoiOSApp - GenerateRunScript - - true - - - - - - - $(ArtifactsDir)/tests/coreclr/obj/$(TargetOS).$(Platform).$(Configuration)/Managed/build/iOSApps/$(TestProjectName)/AppBundle - - - true @@ -43,6 +20,8 @@ $(NoWarn);CS2008 + + false - - - - - @@ -486,19 +354,6 @@ _CMDDIR=%(TestDirectories.Identity) - - - - + @@ -530,7 +385,6 @@ - @@ -631,6 +485,11 @@ $(GroupBuildCmd) "/p:IlcMultiModule=true" $(GroupBuildCmd) "/p:IlcUseServerGc=false" $(GroupBuildCmd) "/p:BuildNativeAotFrameworkObjects=true" + $(GroupBuildCmd) /p:ContinuousIntegrationBuild=true + $(GroupBuildCmd) "/p:BuildTestsOnHelix=true" + $(GroupBuildCmd) "/p:MonoForceInterpreter=true" + $(GroupBuildCmd) "/p:RunAOTCompilation=true" + $(GroupBuildCmd) "/p:DevTeamProvisioning=$(DevTeamProvisioning)" diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 3549319d01c23c..6bd846800fce0c 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1182,9 +1182,6 @@ https://github.com/dotnet/runtime/issues/88775 - - https://github.com/dotnet/runtime/issues/88689 - https://github.com/dotnet/runtime/issues/90308 @@ -1835,12 +1832,6 @@ Mono doesn't support interop BestFitMapping and ThrowOnUnmappableChar attributes - - https://github.com/dotnet/runtime/issues/70279 - - - Tests coreclr's handling of switches on natively sized integers - https://github.com/dotnet/runtime/issues/70820 @@ -2107,9 +2098,6 @@ needs triage - - https://github.com/dotnet/runtime/issues/74687 - https://github.com/dotnet/runtime/issues/71656 diff --git a/src/tools/illink/src/linker/Linker.Steps/DescriptorMarker.cs b/src/tools/illink/src/linker/Linker.Steps/DescriptorMarker.cs index f07200ab47e4b8..806fd3e520cc45 100644 --- a/src/tools/illink/src/linker/Linker.Steps/DescriptorMarker.cs +++ b/src/tools/illink/src/linker/Linker.Steps/DescriptorMarker.cs @@ -103,6 +103,18 @@ void MarkAndPreserveAll (TypeDefinition type, XPathNavigator nav) protected override TypeDefinition? ProcessExportedType (ExportedType exported, AssemblyDefinition assembly, XPathNavigator nav) { _context.MarkingHelpers.MarkExportedType (exported, assembly.MainModule, new DependencyInfo (DependencyKind.XmlDescriptor, _xmlDocumentLocation), GetMessageOriginForPosition (nav)); + + // If a nested exported type is marked, then the declaring type must also be marked otherwise cecil will write out an invalid exported type table + // and anything that tries to read the assembly with cecil will crash + if (exported.DeclaringType != null) { + var currentType = exported.DeclaringType; + while (currentType != null) { + var parent = currentType.DeclaringType; + _context.MarkingHelpers.MarkExportedType (currentType, assembly.MainModule, new DependencyInfo(DependencyKind.DeclaringType, currentType), GetMessageOriginForPosition (nav)); + currentType = parent; + } + } + return base.ProcessExportedType (exported, assembly, nav); } diff --git a/src/tools/illink/src/linker/Linker/LinkContext.cs b/src/tools/illink/src/linker/Linker/LinkContext.cs index 0301f76f0a9f55..c8282d4e40e009 100644 --- a/src/tools/illink/src/linker/Linker/LinkContext.cs +++ b/src/tools/illink/src/linker/Linker/LinkContext.cs @@ -386,7 +386,7 @@ public void SetAction (AssemblyDefinition assembly, AssemblyAction defaultAction Annotations.SetAction (assembly, action); } #endif - public AssemblyAction CalculateAssemblyAction (AssemblyDefinition assembly) + public virtual AssemblyAction CalculateAssemblyAction (AssemblyDefinition assembly) { if (_actions.TryGetValue (assembly.Name.Name, out AssemblyAction action)) { if (IsCPPCLIAssembly (assembly.MainModule) && action != AssemblyAction.Copy && action != AssemblyAction.Skip) { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ForwardedNestedTypeLibrary.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ForwardedNestedTypeLibrary.cs new file mode 100644 index 00000000000000..be363bd6ddefdb --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/Dependencies/ForwardedNestedTypeLibrary.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +#if INCLUDE_FORWARDERS +[assembly: System.Runtime.CompilerServices.TypeForwardedTo (typeof (Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ForwardedNestedTypeLibrary))] +#endif + +#if INCLUDE_REFERENCE_IMPL +namespace Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +public class ForwardedNestedTypeLibrary +{ + public class NestedOne + { + public class NestedTwo + { + public class NestedThree + { + } + } + } +} +#endif diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/NestedTypeForwarder.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/NestedTypeForwarder.cs new file mode 100644 index 00000000000000..07fed9dc501b64 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/NestedTypeForwarder.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.TypeForwarding; + +[SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/ForwardedNestedTypeLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] +[SetupCompileBefore ("Implementation.dll", new[] { "Dependencies/ForwardedNestedTypeLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] +[SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwardedNestedTypeLibrary.cs" }, references: new[] { "Implementation.dll" }, defines: new[] { "INCLUDE_FORWARDERS" })] +[SetupLinkerDescriptorFile("NestedTypeForwarder.xml")] +[KeptAssembly("Forwarder.dll")] +[KeptTypeInAssembly("Forwarder.dll", "Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ForwardedNestedTypeLibrary")] +[KeptTypeInAssembly("Forwarder.dll", "Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ForwardedNestedTypeLibrary/NestedOne")] +[KeptTypeInAssembly("Forwarder.dll", "Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ForwardedNestedTypeLibrary/NestedOne/NestedTwo")] +[KeptTypeInAssembly("Forwarder.dll", "Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ForwardedNestedTypeLibrary/NestedOne/NestedTwo/NestedThree")] +public class NestedTypeForwarder +{ + public static void Main () + { + + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/NestedTypeForwarder.xml b/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/NestedTypeForwarder.xml new file mode 100644 index 00000000000000..9249b63cbc0505 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/NestedTypeForwarder.xml @@ -0,0 +1,6 @@ + + + + + +