mirror of https://github.com/icsharpcode/ILSpy.git
Tree:
85198271c9
christophwille/closedhierarchies
christophwille/membench
compound-assignment-operators
fix/1982-params-attribute-args
fix/2040-invalid-xml-characters
fix/2093-navigateto-reference-assembly
fix/2362-xalz-references
fix/2372-address-taken-by
fix/3282-indexer-optional-arguments
fix/3568-record-member-order
fix/4059-deconstruct-out-slots
fix/lambda-parameter-syntax
fix/scroll-children-on-expand
gh-pages
ldmembertoken
master
natural-type-lambdas-methods
null-coalescing-assignment
release/10.1
release/6.2
release/7.1
release/7.2
release/8.1
substring-optimizations
tests/829-async-method-builder-override
tests/829-collection-expressions
tests/829-compound-assignment-operators
tests/829-coverage-audit
tests/829-expression-tree-named-optional-args
tests/829-expression-variables-in-initializers
tests/829-extended-property-patterns
tests/829-extension-members
tests/829-extension-operators
tests/829-file-local-types
tests/829-improved-definite-assignment
tests/829-improved-overload-candidates
tests/829-inline-arrays
tests/829-interpolated-string-improvements
tests/829-lambda-param-modifiers
tests/829-list-patterns
tests/829-lock-object
tests/829-mixed-deconstruction
tests/829-null-coalescing-assignment
tests/829-null-conditional-assignment
tests/829-object-initializer-indexer
tests/829-overload-resolution-priority
tests/829-params-collections
tests/829-pattern-matching-improvements
tests/829-primary-constructors
tests/829-ref-unsafe-in-iterators-async
tests/829-sealed-record-tostring
tests/829-target-typed-conditional
tests/829-tuple-comparison
win-a11y-textsize
1.0-Beta
1.0-M1
1.0-M2
1.0-M3
1.0.0
2.0.0
2.1
2.2
2.3
2.3.1
3.0-Preview1
3.0-Preview2
3.0.2
v10.0
v10.0-preview1
v10.0-preview2
v10.0-preview3
v10.0.1
v10.1
v10.1.1
v11.0
v11.0-preview1
v11.0-rc
v2.3.2
v2.4
v3.0
v3.0-beta1
v3.0-beta2
v3.0-beta2a
v3.0-beta3
v3.0-beta4
v3.0.1
v3.1-beta1
v3.1-final
v3.1-rc
v3.2-beta
v3.2-rc
v3.2.0
v4.0
v4.0-alpha1
v4.0-beta1
v4.0-beta2
v4.0-beta3
v4.0-rc1
v4.0-rc2
v4.0.1
v5.0
v5.0-preview1
v5.0-preview2
v5.0-preview3
v5.0-preview4
v5.0-rc1
v5.0.1
v5.0.2
v6.0
v6.0-preview1
v6.0-preview2
v6.0-preview3
v6.0-preview4
v6.0-rc1
v6.1
v6.2
v6.2-preview1
v6.2-preview2
v6.2.1
v7.0
v7.0-preview1
v7.0-preview2
v7.0-preview3
v7.0-rc1
v7.0-rc2
v7.1
v7.2
v7.2-preview1
v7.2-preview2
v7.2-preview3
v7.2-preview4
v7.2-rc
v7.2.1
v8.0
v8.0-preview1
v8.0-preview2
v8.0-preview3
v8.0-preview4
v8.0-rc1
v8.1
v8.1.1
v8.2
v9.0
v9.0-preview1
v9.0-preview2
v9.0-preview3
v9.0-rc
v9.1
${ noResults }
3 Commits (85198271c9b376fc1a7bf2daa9a744cf9a8f6210)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
85198271c9 |
Fix #3320: detect dynamic await in pre-Roslyn state machines
The C# 5 compiler loads the awaiter into a fresh local before every dynamic call site and before the ICriticalNotifyCompletion type test, and the early non-aggressive inlining refuses to fold those copies. Every await matcher identifies an await by the identity of its awaiter variable, so each copy hid the awaiter from the matcher and the whole await stayed undetected. Each such copy has one store and one load, and after the dynamic call sites are collapsed that load sits in the next instruction, which is the case ILInlining.InlineOne already handles, including the check that the source is not overwritten first. Folding is restricted to the IsCompleted and GetResult call sites and the completion-interface type tests, so dynamic calls in user code keep their locals: a blanket fold retypes unrelated locals of the surrounding method (an enum local decompiled as int plus a cast). Two smaller divergences from the Roslyn shape sat behind that one: the merge block of the AwaitOnCompleted/AwaitUnsafeOnCompleted diamond clears doFinallyBodies before its leave, and the awaiter is restored from its object-typed field with unbox.any rather than castclass. The ILPretty fixture is the state machine from the issue's assembly with its external types stubbed out, so the fix stays guarded where the legacy compiler is unavailable. The Pretty fixture for dynamic await now runs the pre-Roslyn configurations as well, which is the end-to-end guard on Windows; its expected output records that the compiler copies an awaited dynamic value into a local first and emits no debug name for it. The dead stores an optimized build leaves before a try block containing an await are not specific to dynamic - a plain await on a Task produces the same three - so they sit behind an EXPECTED_OUTPUT-only block. Checked against real legacy csc output (/o- and /o+): dynamic await in plain code, loops, try/catch, try/finally and using. Assisted-by: Claude:claude-opus-5[1m]:Claude Code |
1 day ago |
|
|
8ab3f93f5e |
Fix #3704: keep the static type of a dynamic call target
A dynamic call site that names a type passes typeof(T) as its target. When a later argument contains control flow, the compiler stores that typeof in a temporary. ILInlining does not undo this: it inlines a store only into the immediately following instruction, so an intervening statement leaves the typeof behind, and the expression builder printed the temporary instead of the type. Substituting the typeof back into the target slot is not an option either, because a call there has side effects and blocks inlining of the remaining arguments. The type is therefore stored on the instruction. Object creation already did this, but kept the field private, so the expression builder re-derived the type from the target argument and failed on the spilled form. Member invocation gets the same field. Both drop the target from Arguments, so the dead-argument handling removes the store; ArgumentInfo keeps its entry for the target, because the invocation symbol is built from it. The type of an object creation is not nullable: the transform returns before constructing the instruction when it cannot match the typeof, and substitutes the unknown type otherwise. An unresolvable type therefore reaches the expression builder as the unresolved type it is, and prints as `?` like any other, rather than being indistinguishable from a missing one. Those two are also the only binder method kinds that ever see CSharpArgumentInfoFlags.IsStaticType. Every other way of naming a type as the receiver binds statically and leaves only a conversion of the dynamic operand behind. Assisted-by: Claude:claude-opus-5[1m]:Claude Code |
3 weeks ago |
|
|
c52a0f29f6 |
Fix #1388, #1928: decompile await on dynamic expressions
Awaiting a dynamic value lowers GetAwaiter/IsCompleted/GetResult to dynamic callsites, which async decompilation could not recognize: await detection ran before DynamicCallSiteTransform, so the await was emitted as a raw state machine (or crashed in AnalyzeAwaitBlock, #1388). AnalyzeStateMachine now collapses the awaiter callsites per block, folds the runtime ICriticalNotifyCompletion branch the compiler emits for an awaiter not statically known to implement it into the canonical single call, and re-joins the branch chains the collapse leaves so each dynamic await sits in one block. DetectAwaitPattern matches the dynamic GetAwaiter/IsCompleted/GetResult shape and emits `await expr`; a synthesized dynamic GetResult method gives the await and its local the dynamic type. DynamicCallSiteTransform also follows callsite targets spilled into state-machine locals, so an awaited value flowing into a dynamic callsite (e.g. d.Result = await ..., #1928) decompiles too. Assisted-by: Claude:claude-fable-5:Claude Code |
2 months ago |