Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IL Emit support for MethodInfo.Invoke() and friends #67917

Merged
merged 23 commits into from
May 9, 2022

Conversation

steveharter
Copy link
Member

@steveharter steveharter commented Apr 12, 2022

For performance, the Invoke() method on MethodInfo, PropertyInfo, ConstructorInfo and DynamicMethod now call to IL-emitted invoke code. A canonical invoke of a method with 4 different types of parameters (int, string, struct, class) is ~3.2x faster including the allocation of the object[] to hold the parameters. With a cached object[], performance increases to ~4.4x. A zero-parameter constructor is ~4.2x faster.

This builds upon the recent refactoring work and helps move the code base to supporting byref-like types in the goals.

Not done in this PR:

  • IL emit support for setting\getting fields will be in a subsequent PR.
  • Mono is currently not supported but is planned in V7. Most of the managed code is now shared with Mono to make this easier.

Notes on the algorithm for determining when to use IL Emit:

  • IL Emit is only used if RuntimeFeature.IsDynamicCodeCompiled is true. The other alternative is to use IsDynamicCodeSupported however using IsDynamicCodeCompiled is consistent with other areas including DependencyInjection where Mono interpreter scenarios are considered.
  • The first access of a member does not cause IL to be generated. This helps with startup performance and other cases where a given member is accessed only once through reflection.
    • Currently only System.Diagnostics.Tracing.EventSourceAttribute is invoked through reflection during startup in CoreClr, and no member is called more than once.
    • Pending feedback, we could generate the IL on a background thread and continue to use the native "slow path" until emit is done.
    • Pending feedback and research, we may switch from using DynamicMethod to the native IL emit code (which would need to be done on both Mono and CoreClr). If that switch to native does not occur, trimming support using the IsDynamicCodeCompiled feature switch will likely be used to remove references to DynamicMethod.

Testing:

  • The IL Emit algorithm above was temporary modified to only use IL Emit, so any method invoked through reflection went through the IL path. With the commit containing those temporary changes, all CI checks passed.
  • Tests were added that were missing coverage.
  • After this PR is in, a new PR will be created that refactors the reflection tests in System.Reflection.Tests and System.Runtime.Tests to run under both emit and native invoke.

Benchmarks shown below. Notes:

  • Allocations are the same.
  • The "before" benchmarks compare to a baseline taken before the prior refactoring PR mentioned above meaning both PRs together are included in the "after" results.
  • The zero-parameter Activator.CreateInstance that was previously optimized in v6 is not affected by this PR and used to be 6x faster than using ConstructorInfo. With this PR, however, Activator.CreateInstance is now just ~1.5x (50%) faster than ConstructorInfo. The non-zero parameter constructors for Activator.CreateInstance (which were not optimized in v6) have improved significantly with this PR since they forward to ConstructorInfo and thus are still slower than using the equivalent ConstructorInfo directly.
Click for Emit Invoke (fast-path) benchmarks
|                                                                       Method |        Job |              Toolchain |       Mean |      Error |     StdDev |     Median |        Min |        Max | Ratio | RatioSD |  Gen 0 | Allocated | Alloc Ratio |
|----------------------------------------------------------------------------- |----------- |----------------------- |-----------:|-----------:|-----------:|-----------:|-----------:|-----------:|------:|--------:|-------:|----------:|------------:|
|                                                              Method0_NoParms | Job-IJOXSR |      \main\corerun.exe |  56.716 ns | 1.0106 ns  | 0.9926 ns  |  56.846 ns |  55.592 ns |  58.847 ns |  3.20 |    0.11 | 0.0052 |      56 B |        1.00 |
|                                                              Method0_NoParms | Job-XAYPEN | \newinvoke\corerun.exe |  17.705 ns | 0.4507 ns  | 0.5190 ns  |  17.544 ns |  17.118 ns |  18.787 ns |  1.00 |    0.00 | 0.0053 |      56 B |        1.00 |
|                                                                              |            |                        |            |            |            |            |            |            |       |         |        |           |             |
|                                        StaticMethod4_int_string_struct_class | Job-AEFDZT |      \main\corerun.exe | 200.804 ns |  3.3102 ns |  2.7642 ns | 200.939 ns | 194.044 ns | 205.150 ns |  3.22 |    0.07 | 0.0146 |     160 B |        1.00 |
|                                        StaticMethod4_int_string_struct_class | Job-VAFQFP | \newinvoke\corerun.exe |  62.374 ns |  0.8406 ns |  0.7452 ns |  62.538 ns |  61.110 ns |  63.808 ns |  1.00 |    0.00 | 0.0151 |     160 B |        1.00 |
|                                                                              |            |                        |            |            |            |            |            |            |       |         |        |           |             |
|             StaticMethod4_PreInstantiatedObjectArray_int_string_struct_class | Job-VXDOZF |      \main\corerun.exe |  185.81 ns |   4.029 ns |   4.640 ns |  186.60 ns | 177.258 ns |  195.81 ns |  4.38 |    0.12 |      - |         - |          NA |
|             StaticMethod4_PreInstantiatedObjectArray_int_string_struct_class | Job-NPYPXF | \newinvoke\corerun.exe |   42.56 ns |   0.539 ns |   0.505 ns |   42.77 ns |  41.515 ns |   43.15 ns |  1.00 |    0.00 |      - |         - |          NA |
|                                                                              |            |                        |            |            |            |            |            |            |       |         |        |           |             |
|                            StaticMethod4_ByRefParams_int_string_struct_class | Job-AEFDZT |      \main\corerun.exe | 397.004 ns | 10.2368 ns | 11.7887 ns | 392.676 ns | 383.179 ns | 420.803 ns |  1.56 |    0.05 | 0.0244 |     264 B |        1.00 |
|                            StaticMethod4_ByRefParams_int_string_struct_class | Job-VAFQFP | \newinvoke\corerun.exe | 255.780 ns |  3.0709 ns |  2.5643 ns | 255.959 ns | 251.425 ns | 259.220 ns |  1.00 |    0.00 | 0.0247 |     264 B |        1.00 |
|                                                                              |            |                        |            |            |            |            |            |            |       |         |        |           |             |
| StaticMethod4_ByRefParams_PreInstantiatedObjectArray_int_string_struct_class | Job-AEFDZT |      \main\corerun.exe | 367.753 ns |  7.3759 ns |  7.5745 ns | 365.351 ns | 357.625 ns | 381.999 ns |  1.66 |    0.04 | 0.0091 |     104 B |        1.00 |
| StaticMethod4_ByRefParams_PreInstantiatedObjectArray_int_string_struct_class | Job-VAFQFP | \newinvoke\corerun.exe | 222.551 ns |  3.3380 ns |  3.1224 ns | 221.611 ns | 216.767 ns | 228.102 ns |  1.00 |    0.00 | 0.0098 |     104 B |        1.00 |
|                                                                              |            |                        |            |            |            |            |            |            |       |         |        |           |             |
|                                                    StaticMethod1_NullableInt | Job-AEFDZT |      \main\corerun.exe | 162.207 ns |  1.2924 ns |  1.0090 ns | 162.420 ns | 159.462 ns | 163.183 ns |  1.75 |    0.01 | 0.0072 |      80 B |        1.00 |
|                                                    StaticMethod1_NullableInt | Job-VAFQFP | \newinvoke\corerun.exe |  92.576 ns |  0.8409 ns |  0.7454 ns |  92.645 ns |  91.030 ns |  93.980 ns |  1.00 |    0.00 | 0.0076 |      80 B |        1.00 |
|                                                                              |            |                        |            |            |            |            |            |            |       |         |        |           |             |
|                                                               Ctor0_NoParams | Job-AEFDZT |      \main\corerun.exe |  62.375 ns |  1.5021 ns |  1.6696 ns |  61.603 ns |  60.901 ns |  65.839 ns |  4.18 |    0.12 | 0.0053 |      56 B |        1.00 |
|                                                               Ctor0_NoParams | Job-VAFQFP | \newinvoke\corerun.exe |  14.928 ns |  0.5837 ns |  0.6721 ns |  14.697 ns |  14.234 ns |  16.242 ns |  1.00 |    0.00 | 0.0053 |      56 B |        1.00 |
|                                                                              |            |                        |            |            |            |            |            |            |       |         |        |           |             |
|                                       Ctor0_ActivatorCreateInstance_NoParams | Job-AEFDZT |      \main\corerun.exe |   9.599 ns |  0.2330 ns |  0.2590 ns |   9.611 ns |   9.201 ns |  10.052 ns |  1.11 |    0.02 | 0.0054 |      56 B |        1.00 |
|                                       Ctor0_ActivatorCreateInstance_NoParams | Job-VAFQFP | \newinvoke\corerun.exe |   8.700 ns |  0.1857 ns |  0.1646 ns |   8.733 ns |   8.395 ns |   8.999 ns |  1.00 |    0.00 | 0.0054 |      56 B |        1.00 |
|                                                                              |            |                        |            |            |            |            |            |            |       |         |        |           |             |
|                                                Ctor4_int_string_struct_class | Job-AEFDZT |      \main\corerun.exe | 224.732 ns |  2.7349 ns |  2.5582 ns | 224.325 ns | 221.656 ns | 230.205 ns |  2.98 |    0.04 | 0.0200 |     216 B |        1.00 |
|                                                Ctor4_int_string_struct_class | Job-VAFQFP | \newinvoke\corerun.exe |  75.505 ns |  1.0811 ns |  1.0113 ns |  75.766 ns |  73.839 ns |  77.349 ns |  1.00 |    0.00 | 0.0205 |     216 B |        1.00 |
|                                                                              |            |                        |            |            |            |            |            |            |       |         |        |           |             |
|                                                Ctor4_ActivatorCreateInstance | Job-AEFDZT |      \main\corerun.exe | 478.473 ns | 13.2152 ns | 15.2187 ns | 486.096 ns | 457.438 ns | 498.302 ns |  1.70 |    0.06 | 0.0539 |     576 B |        1.00 |
|                                                Ctor4_ActivatorCreateInstance | Job-VAFQFP | \newinvoke\corerun.exe | 285.902 ns |  4.1302 ns |  3.4489 ns | 285.196 ns | 281.876 ns | 293.944 ns |  1.00 |    0.00 | 0.0545 |     576 B |        1.00 |
|                                                                              |            |                        |            |            |            |            |            |            |       |         |        |           |             |
|                                                             Property_Get_int | Job-AEFDZT |      \main\corerun.exe |  76.706 ns |  0.9476 ns |  0.8864 ns |  76.794 ns |  75.429 ns |  78.145 ns |  3.93 |    0.04 | 0.0075 |      80 B |        1.00 |
|                                                             Property_Get_int | Job-VAFQFP | \newinvoke\corerun.exe |  19.506 ns |  0.3646 ns |  0.3410 ns |  19.623 ns |  19.083 ns |  20.200 ns |  1.00 |    0.00 | 0.0076 |      80 B |        1.00 |
|                                                                              |            |                        |            |            |            |            |            |            |       |         |        |           |             |
|                                                           Property_Get_class | Job-AEFDZT |      \main\corerun.exe |  55.778 ns |  0.5311 ns |  0.4708 ns |  55.938 ns |  54.633 ns |  56.175 ns |  3.24 |    0.03 | 0.0052 |      56 B |        1.00 |
|                                                           Property_Get_class | Job-VAFQFP | \newinvoke\corerun.exe |  17.203 ns |  0.2297 ns |  0.2037 ns |  17.177 ns |  16.930 ns |  17.537 ns |  1.00 |    0.00 | 0.0053 |      56 B |        1.00 |
|                                                                              |            |                        |            |            |            |            |            |            |       |         |        |           |             |
|                                                             Property_Set_int | Job-AEFDZT |      \main\corerun.exe |  97.520 ns |  1.9562 ns |  1.7341 ns |  97.166 ns |  95.522 ns | 101.273 ns |  2.75 |    0.06 | 0.0075 |      80 B |        1.00 |
|                                                             Property_Set_int | Job-VAFQFP | \newinvoke\corerun.exe |  35.510 ns |  0.5620 ns |  0.4693 ns |  35.514 ns |  34.787 ns |  36.585 ns |  1.00 |    0.00 | 0.0076 |      80 B |        1.00 |
|                                                                              |            |                        |            |            |            |            |            |            |       |         |        |           |             |
|                                                           Property_Set_class | Job-AEFDZT |      \main\corerun.exe |  87.269 ns |  0.6875 ns |  0.5741 ns |  87.344 ns |  86.270 ns |  88.369 ns |  2.92 |    0.04 | 0.0050 |      56 B |        1.00 |
|                                                           Property_Set_class | Job-VAFQFP | \newinvoke\corerun.exe |  29.897 ns |  0.5348 ns |  0.4741 ns |  30.014 ns |  29.059 ns |  30.966 ns |  1.00 |    0.00 | 0.0052 |      56 B |        1.00 |

The same benchmarks as above but using Native Invoke ("slow path"). These are essentially unchanged to moderately faster (up to 1.3x faster):

Click for Native Invoke (slow-path) benchmarks used when emit is not available
|                                                                       Method |        Job |                    Toolchain |       Mean |     Error |    StdDev |     Median |        Min |        Max | Ratio | RatioSD |  Gen 0 | Allocated | Alloc Ratio |
|----------------------------------------------------------------------------- |----------- |----------------------------- |-----------:|----------:|----------:|-----------:|-----------:|-----------:|------:|--------:|-------:|----------:|------------:|
|                                                              Method0_NoParms | Job-PTQTZU |            \main\corerun.exe |  58.092 ns | 0.3453 ns | 0.3230 ns |  58.053 ns |  57.638 ns |  58.750 ns |  1.11 |    0.02 | 0.0052 |      56 B |        1.00 |
|                                                              Method0_NoParms | Job-CAURUS | \newinvokenative\corerun.exe |  52.221 ns | 0.8399 ns | 0.7857 ns |  51.850 ns |  51.468 ns |  53.777 ns |  1.00 |    0.00 | 0.0053 |      56 B |        1.00 |
|                                                                              |            |                              |            |           |           |            |            |            |       |         |        |           |             |
|                                        StaticMethod4_int_string_struct_class | Job-PTQTZU |            \main\corerun.exe | 206.566 ns | 1.0410 ns | 0.8692 ns | 206.430 ns | 205.517 ns | 208.009 ns |  1.32 |    0.01 | 0.0150 |     160 B |        1.00 |
|                                        StaticMethod4_int_string_struct_class | Job-CAURUS | \newinvokenative\corerun.exe | 157.069 ns | 0.9537 ns | 0.8921 ns | 157.061 ns | 155.838 ns | 158.818 ns |  1.00 |    0.00 | 0.0152 |     160 B |        1.00 |
|                                                                              |            |                              |            |           |           |            |            |            |       |         |        |           |             |
|             StaticMethod4_PreInstantiatedObjectArray_int_string_struct_class | Job-PTQTZU |            \main\corerun.exe | 177.330 ns | 0.5811 ns | 0.5151 ns | 177.314 ns | 176.741 ns | 178.500 ns |  1.29 |    0.03 |      - |         - |          NA |
|             StaticMethod4_PreInstantiatedObjectArray_int_string_struct_class | Job-CAURUS | \newinvokenative\corerun.exe | 137.053 ns | 2.7575 ns | 2.9505 ns | 136.904 ns | 133.502 ns | 144.133 ns |  1.00 |    0.00 |      - |         - |          NA |
|                                                                              |            |                              |            |           |           |            |            |            |       |         |        |           |             |
|                            StaticMethod4_ByRefParams_int_string_struct_class | Job-PTQTZU |            \main\corerun.exe | 389.498 ns | 1.3818 ns | 1.1538 ns | 389.246 ns | 387.904 ns | 392.190 ns |  1.03 |    0.00 | 0.0249 |     264 B |        1.00 |
|                            StaticMethod4_ByRefParams_int_string_struct_class | Job-CAURUS | \newinvokenative\corerun.exe | 379.837 ns | 1.3258 ns | 1.1071 ns | 379.731 ns | 378.264 ns | 382.439 ns |  1.00 |    0.00 | 0.0247 |     264 B |        1.00 |
|                                                                              |            |                              |            |           |           |            |            |            |       |         |        |           |             |
| StaticMethod4_ByRefParams_PreInstantiatedObjectArray_int_string_struct_class | Job-PTQTZU |            \main\corerun.exe | 362.005 ns | 1.5980 ns | 1.3344 ns | 361.557 ns | 360.180 ns | 364.869 ns |  1.05 |    0.00 | 0.0088 |     104 B |        1.00 |
| StaticMethod4_ByRefParams_PreInstantiatedObjectArray_int_string_struct_class | Job-CAURUS | \newinvokenative\corerun.exe | 344.818 ns | 0.6792 ns | 0.5672 ns | 344.909 ns | 343.895 ns | 346.002 ns |  1.00 |    0.00 | 0.0098 |     104 B |        1.00 |
|                                                                              |            |                              |            |           |           |            |            |            |       |         |        |           |             |
|                                                    StaticMethod1_NullableInt | Job-PTQTZU |            \main\corerun.exe | 163.528 ns | 0.9125 ns | 0.8089 ns | 163.209 ns | 162.594 ns | 165.354 ns |  1.06 |    0.01 | 0.0072 |      80 B |        1.00 |
|                                                    StaticMethod1_NullableInt | Job-CAURUS | \newinvokenative\corerun.exe | 154.594 ns | 0.6523 ns | 0.6102 ns | 154.528 ns | 153.795 ns | 155.615 ns |  1.00 |    0.00 | 0.0074 |      80 B |        1.00 |
|                                                                              |            |                              |            |           |           |            |            |            |       |         |        |           |             |
|                                                               Ctor0_NoParams | Job-PTQTZU |            \main\corerun.exe |  61.867 ns | 0.2478 ns | 0.2318 ns |  61.897 ns |  61.528 ns |  62.280 ns |  1.05 |    0.01 | 0.0053 |      56 B |        1.00 |
|                                                               Ctor0_NoParams | Job-CAURUS | \newinvokenative\corerun.exe |  59.037 ns | 0.4323 ns | 0.3610 ns |  58.854 ns |  58.695 ns |  59.693 ns |  1.00 |    0.00 | 0.0054 |      56 B |        1.00 |
|                                                                              |            |                              |            |           |           |            |            |            |       |         |        |           |             |
|(just for comparison\didn't change)    Ctor0_ActivatorCreateInstance_NoParams | Job-PTQTZU |            \main\corerun.exe |   9.542 ns | 0.0464 ns | 0.0411 ns |   9.539 ns |   9.453 ns |   9.632 ns |  1.07 |    0.02 | 0.0053 |      56 B |        1.00 |
|(just for comparison\didn't change)    Ctor0_ActivatorCreateInstance_NoParams | Job-CAURUS | \newinvokenative\corerun.exe |   8.946 ns | 0.1946 ns | 0.1820 ns |   8.881 ns |   8.742 ns |   9.265 ns |  1.00 |    0.00 | 0.0053 |      56 B |        1.00 |
|                                                                              |            |                              |            |           |           |            |            |            |       |         |        |           |             |
|                                                Ctor4_int_string_struct_class | Job-PTQTZU |            \main\corerun.exe | 227.096 ns | 1.0401 ns | 0.9729 ns | 227.098 ns | 225.820 ns | 228.843 ns |  1.24 |    0.01 | 0.0202 |     216 B |        1.00 |
|                                                Ctor4_int_string_struct_class | Job-CAURUS | \newinvokenative\corerun.exe | 182.462 ns | 0.6690 ns | 0.5930 ns | 182.290 ns | 181.587 ns | 183.641 ns |  1.00 |    0.00 | 0.0205 |     216 B |        1.00 |
|                                                                              |            |                              |            |           |           |            |            |            |       |         |        |           |             |
|                                                Ctor4_ActivatorCreateInstance | Job-PTQTZU |            \main\corerun.exe | 459.764 ns | 2.6074 ns | 2.1773 ns | 459.955 ns | 456.608 ns | 463.282 ns |  1.10 |    0.01 | 0.0533 |     576 B |        1.00 |
|                                                Ctor4_ActivatorCreateInstance | Job-CAURUS | \newinvokenative\corerun.exe | 419.211 ns | 1.4983 ns | 1.2512 ns | 419.277 ns | 416.780 ns | 421.790 ns |  1.00 |    0.00 | 0.0550 |     576 B |        1.00 |
|                                                                              |            |                              |            |           |           |            |            |            |       |         |        |           |             |
|                                                             Property_Get_int | Job-PTQTZU |            \main\corerun.exe |  78.525 ns | 0.2436 ns | 0.2278 ns |  78.502 ns |  78.168 ns |  78.866 ns |  1.07 |    0.01 | 0.0073 |      80 B |        1.00 |
|                                                             Property_Get_int | Job-CAURUS | \newinvokenative\corerun.exe |  73.369 ns | 0.5966 ns | 0.4982 ns |  73.288 ns |  72.702 ns |  74.229 ns |  1.00 |    0.00 | 0.0075 |      80 B |        1.00 |
|                                                                              |            |                              |            |           |           |            |            |            |       |         |        |           |             |
|                                                           Property_Get_class | Job-PTQTZU |            \main\corerun.exe |  59.587 ns | 0.2473 ns | 0.2192 ns |  59.541 ns |  59.254 ns |  59.967 ns |  1.13 |    0.01 | 0.0051 |      56 B |        1.00 |
|                                                           Property_Get_class | Job-CAURUS | \newinvokenative\corerun.exe |  52.866 ns | 0.2004 ns | 0.1874 ns |  52.904 ns |  52.491 ns |  53.093 ns |  1.00 |    0.00 | 0.0052 |      56 B |        1.00 |
|                                                                              |            |                              |            |           |           |            |            |            |       |         |        |           |             |
|                                                             Property_Set_int | Job-PTQTZU |            \main\corerun.exe |  97.264 ns | 0.9386 ns | 0.7837 ns |  97.100 ns |  96.048 ns |  98.601 ns |  1.16 |    0.01 | 0.0075 |      80 B |        1.00 |
|                                                             Property_Set_int | Job-CAURUS | \newinvokenative\corerun.exe |  84.178 ns | 0.5153 ns | 0.4820 ns |  84.259 ns |  83.492 ns |  85.078 ns |  1.00 |    0.00 | 0.0075 |      80 B |        1.00 |
|                                                                              |            |                              |            |           |           |            |            |            |       |         |        |           |             |
|                                                           Property_Set_class | Job-PTQTZU |            \main\corerun.exe |  88.668 ns | 1.0613 ns | 0.8862 ns |  88.263 ns |  87.949 ns |  90.941 ns |  1.05 |    0.01 | 0.0050 |      56 B |        1.00 |
|                                                           Property_Set_class | Job-CAURUS | \newinvokenative\corerun.exe |  84.607 ns | 0.2636 ns | 0.2201 ns |  84.533 ns |  84.301 ns |  85.122 ns |  1.00 |    0.00 | 0.0051 |      56 B |        1.00 |

@steveharter steveharter added this to the 7.0.0 milestone Apr 12, 2022
@steveharter steveharter self-assigned this Apr 12, 2022
@ghost
Copy link

ghost commented Apr 12, 2022

Tagging subscribers to this area: @dotnet/area-system-reflection
See info in area-owners.md if you want to be subscribed.

Issue Details

[WIP; testing]

Author: steveharter
Assignees: steveharter
Labels:

area-System.Reflection

Milestone: 7.0.0

@AaronRobinsonMSFT AaronRobinsonMSFT added the NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) label Apr 12, 2022
il.Emit(OpCodes.Ldarg_1);
if (method.DeclaringType!.IsValueType)
{
il.Emit(OpCodes.Unbox, method.DeclaringType);
Copy link
Member

Choose a reason for hiding this comment

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

The boxing/unboxing of this and return value should be moved to the static code too.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes per discussion the forcing factor will be supporting the new byref-like type APIs possibly using TypedReference. That will enable true by-ref returns (with aliasing) and ability to invoke a target value type (and eventually byref-like types) without boxing or making a copy of the target.

@steveharter
Copy link
Member Author

steveharter commented May 5, 2022

I have built the changes locally to step through the code and to do some ad-hoc testing. I have noticed that the internal reflection frames show up in some places where they were hidden before. Can it be fixed?

For example:

Repro:

Add a test that throws exception to the System.Reflection test project in this repo and run the test.

[Fact]
public static void MyTest()
{
    throw new Exception();
}

Actual output with this PR - the internal reflection frames are in the output:

    Starting:    System.Reflection.Tests (parallel test collections = on, max threads = 20)
      System.Reflection.Tests.ExceptionTests.MyTest [FAIL]
        System.Exception : Exception of type 'System.Exception' was thrown.
        Stack Trace:
          C:\runtime\src\libraries\System.Reflection\tests\ExceptionTests.cs(19,0): at System.Reflection.Tests.Exceptio
  nTests.MyTest()
             at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConst
  ructor)
          C:\runtime\src\libraries\System.Private.CoreLib\src\System\Reflection\MethodInvoker.cs(57,0): at System.Refle
  ction.MethodInvoker.InvokeUnsafe(Object obj, IntPtr* args, BindingFlags invokeAttr)
    Finished:    System.Reflection.Tests

Expected output - no internal reflection frames in the output:

    Starting:    System.Reflection.Tests (parallel test collections = on, max threads = 20)
      System.Reflection.Tests.ExceptionTests.MyTest [FAIL]
        System.Exception : Exception of type 'System.Exception' was thrown.
        Stack Trace:
          C:\runtime\src\libraries\System.Reflection\tests\ExceptionTests.cs(19,0): at System.Reflection.Tests.Exceptio
  nTests.MyTest()
    Finished:    System.Reflection.Tests

Below is a non-Xunit example. The e.StackTrace property remains essentially the same (or consistent) since that displays the inner exception and the call stack hasn't changed too much. However, e.ToString() now shows 2 extra frames in the outer\actual exception section since the try\catch is now in managed code and strictly wraps the lowest-level invocation (either the call to the IL-based delegate or the native runtime).

I believe we could use [StackTraceHidden] so that the e.ToString() version hides the 2 extra frames. Created this issue to track.

using System.Reflection;

MethodInfo mi = typeof(MyClass).GetMethod("ThrowException", BindingFlags.Static | BindingFlags.Public);

// Call and display e.StackTrace
try
{
    mi.Invoke(null, null);
    // or same results with:
    // mi.Invoke(null, BindingFlags.DoNotWrapExceptions, null, null, null);
}
catch (Exception e)
{
    Console.WriteLine(e.StackTrace);
    Console.WriteLine();
}

// Results before reflection changes (or against 6.0):
/*
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at Program.<Main>$(String[] args) in C:\Users\sharter\source\repos\ConsoleApp24\Program.cs:line 14
*/

// Results after reflection changes; consistent but not the same with the "before" results above (since method names\args changed)
/*
   at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at Program.<Main>$(String[] args) in C:\Users\sharter\source\repos\ConsoleApp24\Program.cs:line 14
*/

// Call and display e.ToString()
try
{
    mi.Invoke(null, null);
}
catch (Exception e)
{
    Console.WriteLine(e.ToString()); // Same results if we don't wrap in try\catch.
}

// Results before reflection changes (or against 6.0):
/*
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.Exception: Exception of type 'System.Exception' was thrown.
   at MyClass.ThrowException() in C:\Users\sharter\source\repos\ConsoleApp24\Program.cs:line 80
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at Program.<Main>$(String[] args) in C:\Users\sharter\source\repos\ConsoleApp24\Program.cs:line 43
*/

// Results after reflection changes: NOTE THE 2 EXTRA FRAMES BEFORE THE INNER EXCEPTION
/*
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.Exception: Exception of type 'System.Exception' was thrown.
   at MyClass.ThrowException() in C:\Users\sharter\source\repos\ConsoleApp24\Program.cs:line 80
>> at InvokeStub_MyClass.ThrowException(Object, Object, IntPtr*)
>> at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
   --- End of inner exception stack trace ---
   at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr) in C:\git\runtime_reflection7\src\libraries\System.Private.CoreLib\src\System\Reflection\MethodInvoker.cs:line 69
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) in C:\git\runtime_reflection7\src\libraries\System.Private.CoreLib\src\System\Reflection\RuntimeMethodInfo.cs:line 129
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) in C:\git\runtime_reflection7\src\libraries\System.Private.CoreLib\src\System\Reflection\MethodBase.cs:line 54
   at Program.<Main>$(String[] args) in C:\Users\sharter\source\repos\ConsoleApp24\Program.cs:line 43
*/

// Note that if the reflection is not using Emit yet (for example on the first call to a MethodInfo) the 2nd frame above will be
//  at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
// instead of
//  at InvokeStub_MyClass.ThrowException(Object, Object, IntPtr *)

public class MyClass
{
    public static void ThrowException()
    {
        throw new Exception();
    }
}

@steveharter steveharter changed the title Add IL Emit support for MethodInfo.Invoke() Add IL Emit support for MethodInfo.Invoke() and friends May 5, 2022
@steveharter
Copy link
Member Author

steveharter commented May 9, 2022

Failures appear unrelated:
#67821

@AndyAyersMS
Copy link
Member

Improvements seen on arm64 benchmarks: dotnet/perf-autofiling-issues#5284

@kasperk81
Copy link
Contributor

this is still blocking the sdk update, which is blocking coherency update from 10+ repositories and installer release.

please revert #67917 and #69225 asap

#69251 is tracking issue with unit tests not catching those issues in this repository. that should be fixed first.

jkotas added a commit to jkotas/runtime that referenced this pull request May 15, 2022
@jkotas
Copy link
Member

jkotas commented May 15, 2022

Sorry about that. Submitted revert at #69373

jkotas added a commit that referenced this pull request May 15, 2022
)

* Revert "Fix Assembly.GetCallingAssembly() (#69225)"

This reverts commit 8420dae.

* Revert "Add IL Emit support for MethodInfo.Invoke() and friends (#67917)"

This reverts commit 5195418.
@ghost ghost locked as resolved and limited conversation to collaborators Jun 14, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants