/// <summary> /// Transform local exception variable. /// </summary> void TransformCatchVariable(TryCatchHandler handler, Block entryPoint) { if (!entryPoint.Instructions[0].MatchStLoc(out var exceptionVar, out var exceptionSlotLoad)) { return; } if (!exceptionVar.IsSingleDefinition || exceptionVar.Kind != VariableKind.Local) { return; } if (!exceptionSlotLoad.MatchLdLoc(handler.Variable) || !handler.Variable.IsSingleDefinition || handler.Variable.LoadCount != 1) { return; } handler.Variable = exceptionVar; exceptionVar.Kind = VariableKind.Exception; entryPoint.Instructions.RemoveAt(0); }
/// <summary> /// catch ex : TException when (...) BlockContainer { /// Block entryPoint (incoming: 1) { /// stloc v(ldloc ex) /// ... /// } /// } /// => /// catch v : TException when (...) BlockContainer { /// Block entryPoint (incoming: 1) { /// ... /// } /// } /// </summary> void TransformCatchVariable(TryCatchHandler handler, Block entryPoint, bool isCatchBlock) { if (!handler.Variable.IsSingleDefinition || handler.Variable.LoadCount != 1) { return; // handle.Variable already has non-trivial uses } if (!entryPoint.Instructions[0].MatchStLoc(out var exceptionVar, out var exceptionSlotLoad)) { // Not the pattern with a second exceptionVar. // However, it is still possible that we need to remove a pointless UnboxAny: if (handler.Variable.LoadInstructions.Single().Parent is UnboxAny inlinedUnboxAny) { if (inlinedUnboxAny.Type.Equals(handler.Variable.Type)) { context.Step("TransformCatchVariable - remove inlined UnboxAny", inlinedUnboxAny); inlinedUnboxAny.ReplaceWith(inlinedUnboxAny.Argument); foreach (var range in inlinedUnboxAny.ILRanges) { handler.AddExceptionSpecifierILRange(range); } } } return; } if (exceptionVar.Kind != VariableKind.Local && exceptionVar.Kind != VariableKind.StackSlot) { return; } if (exceptionSlotLoad is UnboxAny unboxAny) { // When catching a type parameter, csc emits an unbox.any instruction if (!unboxAny.Type.Equals(handler.Variable.Type)) { return; } exceptionSlotLoad = unboxAny.Argument; } if (!exceptionSlotLoad.MatchLdLoc(handler.Variable)) { return; } // Check that exceptionVar is only used within the catch block: var allUses = exceptionVar.LoadInstructions .Concat(exceptionVar.StoreInstructions.Cast <ILInstruction>()) .Concat(exceptionVar.AddressInstructions); foreach (var inst in allUses) { if (!inst.IsDescendantOf(handler)) { return; } } context.Step("TransformCatchVariable", entryPoint.Instructions[0]); exceptionVar.Kind = VariableKind.ExceptionLocal; exceptionVar.Type = handler.Variable.Type; handler.Variable = exceptionVar; if (isCatchBlock) { foreach (var offset in entryPoint.Instructions[0].Descendants.SelectMany(o => o.ILRanges)) { handler.AddExceptionSpecifierILRange(offset); } } entryPoint.Instructions.RemoveAt(0); }
protected override void BeginTryCatchHandler(TryCatchHandler inst) { HandleStore(inst.Variable); base.BeginTryCatchHandler(inst); }
/// <summary> /// TryCatchHandler is handled directly in VisitTryCatch /// </summary> protected internal override sealed void VisitTryCatchHandler(TryCatchHandler inst) { throw new NotSupportedException(); }
protected virtual void BeginTryCatchHandler(TryCatchHandler inst) { }