Skip to content

Enable R2R precompilation of blittable ObjC P/Invokes#124770

Draft
davidnguyen-tech wants to merge 10 commits intodotnet:mainfrom
davidnguyen-tech:feature/r2r-objc-pinvoke-stubs
Draft

Enable R2R precompilation of blittable ObjC P/Invokes#124770
davidnguyen-tech wants to merge 10 commits intodotnet:mainfrom
davidnguyen-tech:feature/r2r-objc-pinvoke-stubs

Conversation

@davidnguyen-tech
Copy link
Member

@davidnguyen-tech davidnguyen-tech commented Feb 23, 2026

Problem

TODO: Add measurements on a sample app

Objective-C P/Invokes didn't get precompiled in R2R. On JIT-less platforms like iOS, they would fall back to interpreter with significant performance costs.

They are a common case worth improving - e.g. in dotnet/macios, almost all native calls from managed code go through Objective-C P/Invokes.

Solution

Enable R2R precompilation of blittable Objective-C P/Invoke stubs:

  1. Emit ThrowPendingExceptionObject() in the IL stub after the native call. This mirrors the NativeAOT PInvokeILEmitter.

  2. Remove ShouldCheckForPendingException from Marshaller.IsMarshallingRequired — this blocked GeneratesPInvoke from returning true for blittable ObjC P/Invokes. GeneratesPInvoke is called here to determine whether to emit IL for a P/Invoke.

  3. Add ShouldCheckForPendingException to PInvokeILStubMethodIL.IsMarshallingRequired — this property is read by pInvokeMarshalingRequired, which the JIT uses to decide whether to inline a P/Invoke. When it returns false, the JIT inlines a raw native call (GTF_CALL_UNMANAGED) with no ObjC-specific handling — the precompiled stub with ThrowPendingExceptionObject would exist but never be called. Adding the check here ensures pInvokeMarshalingRequired returns true for ObjC P/Invokes, so the JIT uses the stub.

Result

Startup of a benchmark app improved 1% with the cost of 0,6% increase in size.

Precompilation of blittable ObjC P/Invokes should also have a positive throughput improvement.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables ReadyToRun (R2R) precompilation of Objective-C objc_msgSend-family P/Invoke stubs by emitting a managed pending-exception check after the native call, avoiding a runtime-stub/interpreter fallback on JIT-less Apple platforms.

Changes:

  • Add ObjectiveCMarshal.ThrowPendingExceptionObject() in CoreCLR and expose it to the runtime binder.
  • Update the R2R PInvokeILEmitter to emit a post-call pending-exception check instead of rejecting ObjC P/Invokes.
  • Adjust R2R compilation policy and IL caching behavior to allow ObjC P/Invokes to be attempted and to gracefully fall back when unsupported.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/coreclr/vm/corelib.h Adds CoreLib binder entry for ObjectiveCMarshal.ThrowPendingExceptionObject.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs Simplifies P/Invoke stub IL retrieval logic now that unsupported cases are handled/cached in ILCache.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs Emits ThrowPendingExceptionObject after ObjC native calls; removes ObjC-specific NotSupportedException.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs Allows GeneratesPInvoke for ObjC msgSend P/Invokes even when marshalling is otherwise considered required.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs Caches “requires runtime JIT” decisions for unsupported P/Invoke stub emission via RequiresRuntimeJitException catch.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.cs Implements ThrowPendingExceptionObject() using pending-exception storage and ExceptionDispatchInfo.Throw.

@davidnguyen-tech davidnguyen-tech changed the title Enable R2R precompilation of ObjC objc_msgSend P/Invoke stubs Enable R2R precompilation of objc_msgSend P/Invoke stubs Feb 23, 2026
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to 'os-ios': @vitek-karas, @kotlarmilos, @steveisok, @akoeplinger
See info in area-owners.md if you want to be subscribed.

if (!Marshaller.IsMarshallingRequired(method))
return true;

if (MarshalHelpers.ShouldCheckForPendingException(method.Context.Target, method.GetPInvokeMethodMetadata()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed yesterday, this check here looks wrong. This check is already done as part of Marshaller.IsMarshallingRequired and I believe it should be removed from there. In your case, this method is returning true for pinvokes with non-blittable types that require check for pending exception. It should be returning false instead and I believe this is the reason you are forcefully catching the requires jit exception above. If the checks here would be correct, you shouldn't need to do that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach would newly enable inlining of blittable P/Invokes that require a Pending Exception check. I'm researching the consequences of this decision - it might require additional changes to JIT. It's because JIT inlines a P/Invoke when Marshaller.MarshallingIsRequired return false (below is the proof for completeness):

Copy link
Member Author

@davidnguyen-tech davidnguyen-tech Mar 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current approach with at first glance unnecessary duplicate if (MarshalHelpers.ShouldCheckForPendingException actually prevents JIT from inlining the ObjectiveC P/Invoke, while making sure that the IL Emitter emits the Pending Exception check.

Copy link
Member Author

@davidnguyen-tech davidnguyen-tech Mar 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Synced offline with @jkoritzinsky: there is an option to implement inlining the Pending Exception check through a JIT helper. I will try to pursue this approach to see if it's cleaner.

@BrzVlad
Copy link
Member

BrzVlad commented Feb 24, 2026

Were there also any interpreted pinvokes with non-blittable types ? In case we might need additional fixes, related to the original plan of using LibraryImport.

@AaronRobinsonMSFT
Copy link
Member

Do we need to re-enable or create a new sets of tests for this? We have tests for these APIs, src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI, but I don't know if we've disabled them for R2R (it doesn't look like it), so are we missing them during R2R testing somehow?

/cc @jkoritzinsky

@jkoritzinsky
Copy link
Member

Do we need to re-enable or create a new sets of tests for this? We have tests for these APIs, src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI, but I don't know if we've disabled them for R2R (it doesn't look like it), so are we missing them during R2R testing somehow?

The tests there are falling back to JIT/Interpreter for these stubs (which is correct behavior). We could add testing to validate that the methods were actually generated (src/tests/readytorun/tests/test.cs and the mainv1.csproj project file have examples of the logic (pass the --map option when running cg2 and then validate the expected entries exist as part of running the test).

PureWeen added a commit to davidnguyen-tech/runtime that referenced this pull request Mar 11, 2026
Analyzes reviewer feedback from BrzVlad, AaronRobinsonMSFT, and jkoritzinsky.
Includes recommended code changes and test coverage plan.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen added a commit to PureWeen/runtime that referenced this pull request Mar 13, 2026
…ning findings

- Add LibraryImport vs DllImport analysis explaining why switching macios
  wouldn't solve the R2R precompilation issue
- Add P/Invoke inlining call chain trace showing CorInfoImpl.ReadyToRun.cs
  path (not VM dllimport.cpp) for R2R marshalling decisions
- Document graceful fallback behavior for unsupported marshalling types
- Confirm BrzVlad's approach is both cleaner and more efficient

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 13, 2026 16:29
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.


You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +59 to +65
if (MarshalHelpers.ShouldCheckForPendingException(context.Target, _importMetadata))
{
MetadataType objcMarshalType = context.SystemModule.GetKnownType(
"System.Runtime.InteropServices.ObjectiveC"u8, "ObjectiveCMarshal"u8);
callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken(
objcMarshalType.GetKnownMethod("ThrowPendingExceptionObject"u8, null)));
}
Comment on lines +722 to +724
if (MarshalHelpers.ShouldCheckForPendingException(method.Context.Target, method.GetPInvokeMethodMetadata()))
return true;

catch (RequiresRuntimeJitException)
{
// The P/Invoke IL emitter will throw for known unsupported scenarios.
// We don't propagate the exception and keep methodIL as null - this causes a fall back to JIT during runtime.
Comment on lines +1 to +23
# PR #124770 — Final Review Assessment

## Summary

Analysis of 3 reviewer concerns on PR #124770 ("Enable R2R precompilation of objc_msgSend P/Invoke stubs"), validated against the actual code.

### Historical Context

The `ShouldCheckForPendingException` check was originally added to `IsMarshallingRequired` by Aaron Robinson in commit 4a782d58ac4 (PR #52849, May 2021). At that time, R2R had **no ability** to emit the pending exception check at all. The check served as a two-layer safety net:
1. `IsMarshallingRequired` returns true → `GeneratesPInvoke` returns false → R2R won't try
2. Even if it did try, `EmitIL()` threw `NotSupportedException` → caught in CorInfoImpl

Now that R2R **can** emit the check (via this PR's `ThrowPendingExceptionObject`), the safety net is no longer needed for ObjC P/Invokes — but the original design explains why it was there.

---

## Concern 1: Logic Error in `GeneratesPInvoke` (BrzVlad — inline review)

### BrzVlad's Claim

> "This check here looks wrong. This check is already done as part of `Marshaller.IsMarshallingRequired` and I believe it should be removed from there. In your case, this method is returning true for pinvokes with non-blittable types that require check for pending exception. It should be returning false instead and I believe this is the reason you are forcefully catching the requires jit exception above."

### Verdict: ✅ BrzVlad is correct — the root cause is a conflation of concerns
Copilot AI review requested due to automatic review settings March 17, 2026 12:22
@davidnguyen-tech davidnguyen-tech force-pushed the feature/r2r-objc-pinvoke-stubs branch from 61aa0b6 to 825890f Compare March 17, 2026 12:22
@davidnguyen-tech davidnguyen-tech force-pushed the feature/r2r-objc-pinvoke-stubs branch from 825890f to bef6edf Compare March 17, 2026 12:27
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +59 to +65
if (MarshalHelpers.ShouldCheckForPendingException(context.Target, _importMetadata))
{
MetadataType objcMarshalType = context.SystemModule.GetKnownType(
"System.Runtime.InteropServices.ObjectiveC"u8, "ObjectiveCMarshal"u8);
callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken(
objcMarshalType.GetKnownMethod("ThrowPendingExceptionObject"u8, null)));
}
davidnguyen-tech and others added 3 commits March 17, 2026 13:54
Add a CoreLib helper that retrieves and throws any pending Objective-C
exception. This will be called from R2R-precompiled P/Invoke IL stubs
after objc_msgSend calls, mirroring the existing NativeAOT approach.

Uses ExceptionDispatchInfo.Throw to preserve the original stack trace
and is marked [StackTraceHidden] to keep the throw site clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instead of throwing NotSupportedException for ObjC P/Invokes (which
blocked R2R precompilation entirely), emit a call to
ObjectiveCMarshal.ThrowPendingExceptionObject() after the native call
in the IL stub. This mirrors the NativeAOT PInvokeILEmitter.

The generated stub IL for a blittable objc_msgSend now looks like:
  marshal args → call objc_msgSend → call ThrowPendingExceptionObject → ret

On iOS (no JIT), this eliminates 82 interpreter fallbacks at startup
for a dotnet new maui app.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove ShouldCheckForPendingException from Marshaller.IsMarshallingRequired
— ObjC pending exceptions are a platform concern, not a marshalling concern.
return false for all ObjC P/Invokes, blocking R2R stub generation entirely.

Add the check to PInvokeILStubMethodIL.IsMarshallingRequired instead. This
property is read by pInvokeMarshalingRequired (the JIT-EE interface) to
decide whether the JIT should use the precompiled stub or inline a raw
native call. Without this check, the JIT would inline blittable ObjC
P/Invokes with GTF_CALL_UNMANAGED — a raw call with no pending exception
handling — silently skipping the ThrowPendingExceptionObject call that the
stub contains.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@davidnguyen-tech davidnguyen-tech force-pushed the feature/r2r-objc-pinvoke-stubs branch from bef6edf to e04bc85 Compare March 17, 2026 12:56
@davidnguyen-tech davidnguyen-tech changed the title Enable R2R precompilation of objc_msgSend P/Invoke stubs Enable R2R precompilation of blittable ObjC P/Invokes Mar 17, 2026
PureWeen added a commit to PureWeen/runtime that referenced this pull request Mar 17, 2026
- Document new 3-commit structure and PInvokeILStubMethodIL mechanism
- Mark BrzVlad's recommendations as implemented (GeneratesPInvoke reverted,
  ShouldCheckForPendingException removed from IsMarshallingRequired)
- Add open concerns: code size, P/Invoke frame correctness, non-void IL
  stack risk, JIT helper alternative under investigation
- Test coverage still flagged as missing (merge blocker)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen and others added 3 commits March 17, 2026 14:01
Analyzes reviewer feedback from BrzVlad, AaronRobinsonMSFT, and jkoritzinsky.
Includes recommended code changes and test coverage plan.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ning findings

- Add LibraryImport vs DllImport analysis explaining why switching macios
  wouldn't solve the R2R precompilation issue
- Add P/Invoke inlining call chain trace showing CorInfoImpl.ReadyToRun.cs
  path (not VM dllimport.cpp) for R2R marshalling decisions
- Document graceful fallback behavior for unsupported marshalling types
- Confirm BrzVlad's approach is both cleaner and more efficient

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Document new 3-commit structure and PInvokeILStubMethodIL mechanism
- Mark BrzVlad's recommendations as implemented (GeneratesPInvoke reverted,
  ShouldCheckForPendingException removed from IsMarshallingRequired)
- Add open concerns: code size, P/Invoke frame correctness, non-void IL
  stack risk, JIT helper alternative under investigation
- Test coverage still flagged as missing (merge blocker)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 17, 2026 19:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

PureWeen and others added 3 commits March 18, 2026 10:51
Adds a ReadyToRun test that validates objc_msgSend P/Invoke stubs are
precompiled into the R2R image using crossgen2's --map output.

The test:
- Declares blittable objc_msgSend DllImport methods
- Compiles them with crossgen2 --map
- At runtime, reads the .map file and asserts P/Invoke stub entries exist
- Only runs on macOS (CLRTestTargetUnsupported for non-Apple platforms)

Addresses reviewer feedback from jkoritzinsky and AaronRobinsonMSFT
requesting test coverage for the R2R ObjC P/Invoke precompilation feature.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Validates that blittable objc_msgSend P/Invoke stubs are precompiled
into the ReadyToRun image by checking the crossgen2 --map output for
MethodWithGCInfo entries.

Key details:
- Uses /usr/lib/libobjc.dylib (exact path matched by ShouldCheckForPendingException)
- Checks specifically for MethodWithGCInfo entries (not just any map reference)
- Requires --inputbubble so crossgen2 can resolve ThrowPendingExceptionObject tokens
- macOS-only (CLRTestTargetUnsupported for non-OSX)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Promote regression test verification to a mandatory section in
  copilot-instructions.md with explicit guidance on using baseline
  build artifacts for negative verification
- Add ReadyToRun/crossgen2 test gotchas section covering --inputbubble,
  map entry semantics, and MarshalHelpers.cs string matching
- Add cross-component build warning (crossgen2 + CoreLib changes)
- Add design doc discoverability instruction
- Strengthen CoreLib rebuild guidance to explain libs.pretest necessity
- Add Step 5 (mandatory negative verification) to jit-regression-test skill
- Create src/tests/readytorun/README.md documenting test patterns,
  map validation rules, version bubble constraints, and platform gating

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 18, 2026 19:44
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.


You can also share your feedback on Copilot code review. Take the survey.


# Run a single test manually
export CORE_ROOT=$(pwd)/artifacts/tests/coreclr/<os>.<arch>.Release/Tests/Core_Root
cd artifacts/tests/coreclr/<os>.<arch>.Debug/readytorun/<TestName>/
<CLRTestTargetUnsupported Condition="'$(EnableNativeSanitizers)' != ''">true</CLRTestTargetUnsupported>
</PropertyGroup>
<ItemGroup>
<Compile Include="ObjCPInvokeR2R.cs" />
Comment on lines +1 to +55
# PR #124770 — Final Review Assessment

## Summary

Analysis of the original 3 reviewer concerns on PR #124770 ("Enable R2R precompilation of objc_msgSend P/Invoke stubs"), updated for the March 17 force-push that replaced the earlier escape-hatch/try-catch design with a cleaner 3-commit approach. These March 17 notes describe the PR's force-pushed GitHub state, even if the local checkout still contains the earlier revision.

### Historical Context

The `ShouldCheckForPendingException` check was originally added to `IsMarshallingRequired` by Aaron Robinson in commit 4a782d58ac4 (PR #52849, May 2021). At that time, R2R had **no ability** to emit the pending exception check at all. The check served as a two-layer safety net:
1. `IsMarshallingRequired` returns true → `GeneratesPInvoke` returns false → R2R won't try
2. Even if it did try, `EmitIL()` threw `NotSupportedException` → caught in CorInfoImpl

Now that R2R **can** emit the check (via this PR's `ThrowPendingExceptionObject`), the safety net is no longer needed for ObjC P/Invokes — but the original design explains why it was there.

---

## Concern 1: Logic Error in `GeneratesPInvoke` (BrzVlad — inline review)

### BrzVlad's Claim

> "This check here looks wrong. This check is already done as part of `Marshaller.IsMarshallingRequired` and I believe it should be removed from there. In your case, this method is returning true for pinvokes with non-blittable types that require check for pending exception. It should be returning false instead and I believe this is the reason you are forcefully catching the requires jit exception above."
### Verdict: ✅ BrzVlad's core concern was correct, and the force-push implemented it

**Historical context:** the earlier PR revision conflated two concerns:
1. "Does this P/Invoke need a pending exception check?"
2. "Does this P/Invoke need real parameter/return marshalling?"

That conflation is what created the old `GeneratesPInvoke` escape hatch and the temporary `ReadyToRunCodegenCompilation.cs` try/catch workaround.

### Update (March 17): implemented in the force-push

The restructured PR now:
1. Removes `ShouldCheckForPendingException` from `Marshaller.IsMarshallingRequired`
2. Reverts `GeneratesPInvoke` to `return !Marshaller.IsMarshallingRequired(method)`
3. Adds the ObjC special-case to `PInvokeILStubMethodIL.IsMarshallingRequired`

That third bullet is the key extra insight beyond Vlad's original review comment: without it, the JIT could see a blittable ObjC signature and raw-inline/direct-call it, skipping `ThrowPendingExceptionObject()` entirely.

### Current assessment

- The original layering issue is fixed.
- The removed pieces are the `GeneratesPInvoke` escape hatch, the `ReadyToRunCodegenCompilation.cs` try/catch, and the `CorInfoImpl.ReadyToRun.cs` cleanup.
- The remaining work is validating the new stub shape, code-size impact, P/Invoke frame correctness, and test coverage.

**Severity: ✅ Fixed** — the original logic issue is no longer an open blocker.

---

## Concern 2: Non-Blittable ObjC Scope (BrzVlad — general comment)

### BrzVlad's Claim

> "Were there also any interpreted pinvokes with non-blittable types? In case we might need additional fixes, related to the original plan of using `LibraryImport`."
Comment on lines +1 to +20
# PR #124770 Analysis: Enable R2R precompilation of objc_msgSend P/Invoke stubs

## PR Overview

- **Title**: Enable R2R precompilation of objc_msgSend P/Invoke stubs
- **Author**: davidnguyen-tech
- **Branch**: `feature/r2r-objc-pinvoke-stubs` → `main`
- **Status**: Draft, open
- **March 17 update**: The PR was force-pushed and restructured into 3 cleaner commits:
1. `01a7fe190ad` — add `ThrowPendingExceptionObject()` plus the `corelib.h` binder entry
2. `b41aa09deb6` — emit `ThrowPendingExceptionObject` in `PInvokeILEmitter.EmitPInvokeCall()` and remove the old ObjC blocker
3. `e04bc858067` — remove `ShouldCheckForPendingException` from `Marshaller.IsMarshallingRequired` and move the direct-call suppression to `PInvokeILStubMethodIL.IsMarshallingRequired`
- **Historical note**: The earlier 6-file snapshot below is still useful context, but sections 4-6 are now historical because the force-push removed that part of the design.
- **Scope note**: The March 17 updates in this document describe the PR as force-pushed on GitHub, even if the local checkout you are reading alongside this note still reflects the older pre-force-push revision.

## Problem Statement

On iOS with CoreCLR, the JIT is unavailable. ReadyToRun (R2R) precompiles code ahead of time, but ObjC `objc_msgSend` P/Invoke stubs were explicitly blocked from R2R compilation — the `PInvokeILEmitter` threw `NotSupportedException` for any P/Invoke that needed a pending exception check. This forced these calls to fall back to runtime JIT stub generation, which on JIT-less iOS means falling back to the interpreter, causing a performance penalty.

## Key Concepts
Add P/Invoke marshalling and R2R-specific review rules:
- inputbubble requirement for cross-module CoreLib references
- IsMarshallingRequired prevents JIT from inlining past R2R stubs
- Separation of marshalling vs platform-specific stub requirements
- DllImport detection string matching against MarshalHelpers.cs
- R2R map validation (MethodWithGCInfo vs MethodFixupSignature)
- R2R+NativeAOT P/Invoke emitter alignment checks
- R2R tests need both map validation and runtime verification

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants