void CreateContainerStructure() { List <TryCatch> tryCatchList = new List <TryCatch>(); foreach (var eh in body.ExceptionHandlers) { var tryRange = new Interval(eh.TryStart.Offset, eh.TryEnd != null ? eh.TryEnd.Offset : body.CodeSize); var handlerBlock = new BlockContainer(); handlerBlock.ILRange = new Interval(eh.HandlerStart.Offset, eh.HandlerEnd != null ? eh.HandlerEnd.Offset : body.CodeSize); handlerBlock.Blocks.Add(new Block()); handlerContainers.Add(handlerBlock.ILRange.Start, handlerBlock); if (eh.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Fault || eh.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Finally) { var tryBlock = new BlockContainer(); tryBlock.ILRange = tryRange; if (eh.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Finally) { tryInstructionList.Add(new TryFinally(tryBlock, handlerBlock)); } else { tryInstructionList.Add(new TryFault(tryBlock, handlerBlock)); } continue; } // var tryCatch = tryCatchList.FirstOrDefault(tc => tc.TryBlock.ILRange == tryRange); if (tryCatch == null) { var tryBlock = new BlockContainer(); tryBlock.ILRange = tryRange; tryCatch = new TryCatch(tryBlock); tryCatchList.Add(tryCatch); tryInstructionList.Add(tryCatch); } ILInstruction filter; if (eh.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Filter) { var filterBlock = new BlockContainer(expectedResultType: StackType.I4); filterBlock.ILRange = new Interval(eh.FilterStart.Offset, eh.HandlerStart.Offset); filterBlock.Blocks.Add(new Block()); handlerContainers.Add(filterBlock.ILRange.Start, filterBlock); filter = filterBlock; } else { filter = new LdcI4(1); } tryCatch.Handlers.Add(new TryCatchHandler(filter, handlerBlock, variableByExceptionHandler[eh])); } if (tryInstructionList.Count > 0) { tryInstructionList = tryInstructionList.OrderBy(tc => tc.TryBlock.ILRange.Start).ThenByDescending(tc => tc.TryBlock.ILRange.End).ToList(); nextTry = tryInstructionList[0]; } }
void CreateContainerStructure() { List <TryCatch> tryCatchList = new List <TryCatch>(); foreach (var eh in body.ExceptionRegions) { var tryRange = new Interval(eh.TryOffset, eh.TryOffset + eh.TryLength); var handlerBlock = new BlockContainer(); handlerBlock.ILRange = new Interval(eh.HandlerOffset, eh.HandlerOffset + eh.HandlerLength); handlerBlock.Blocks.Add(new Block()); handlerContainers.Add(handlerBlock.ILRange.Start, handlerBlock); if (eh.Kind == ExceptionRegionKind.Fault || eh.Kind == ExceptionRegionKind.Finally) { var tryBlock = new BlockContainer(); tryBlock.ILRange = tryRange; if (eh.Kind == ExceptionRegionKind.Finally) { tryInstructionList.Add(new TryFinally(tryBlock, handlerBlock) { ILRange = tryRange }); } else { tryInstructionList.Add(new TryFault(tryBlock, handlerBlock) { ILRange = tryRange }); } continue; } // var tryCatch = tryCatchList.FirstOrDefault(tc => tc.TryBlock.ILRange == tryRange); if (tryCatch == null) { var tryBlock = new BlockContainer(); tryBlock.ILRange = tryRange; tryCatch = new TryCatch(tryBlock); tryCatch.ILRange = tryRange; tryCatchList.Add(tryCatch); tryInstructionList.Add(tryCatch); } ILInstruction filter; if (eh.Kind == System.Reflection.Metadata.ExceptionRegionKind.Filter) { var filterBlock = new BlockContainer(expectedResultType: StackType.I4); filterBlock.ILRange = new Interval(eh.FilterOffset, eh.HandlerOffset); filterBlock.Blocks.Add(new Block()); handlerContainers.Add(filterBlock.ILRange.Start, filterBlock); filter = filterBlock; } else { filter = new LdcI4(1); } var handler = new TryCatchHandler(filter, handlerBlock, variableByExceptionHandler[eh]); handler.AddILRange(filter.ILRange); handler.AddILRange(handlerBlock.ILRange); tryCatch.Handlers.Add(handler); tryCatch.AddILRange(handler.ILRange); } if (tryInstructionList.Count > 0) { tryInstructionList = tryInstructionList.OrderBy(tc => tc.TryBlock.ILRange.Start).ThenByDescending(tc => tc.TryBlock.ILRange.End).ToList(); nextTry = tryInstructionList[0]; } }
/// <summary> /// Transform compound assignments where the return value is not being used, /// or where there's an inlined assignment within the setter call. /// /// Patterns handled: /// 1. /// callvirt set_Property(ldloc S_1, binary.op(callvirt get_Property(ldloc S_1), value)) /// ==> compound.op.new(callvirt get_Property(ldloc S_1), value) /// 2. /// callvirt set_Property(ldloc S_1, stloc v(binary.op(callvirt get_Property(ldloc S_1), value))) /// ==> stloc v(compound.op.new(callvirt get_Property(ldloc S_1), value)) /// 3. /// stobj(target, binary.op(ldobj(target), ...)) /// where target is pure /// => compound.op(target, ...) /// </summary> /// <remarks> /// Called by ExpressionTransforms, or after the inline-assignment transform for setters. /// </remarks> internal static bool HandleCompoundAssign(ILInstruction compoundStore, StatementTransformContext context) { if (!context.Settings.MakeAssignmentExpressions || !context.Settings.IntroduceIncrementAndDecrement) { return(false); } if (compoundStore is CallInstruction && compoundStore.SlotInfo != Block.InstructionSlot) { // replacing 'call set_Property' with a compound assignment instruction // changes the return value of the expression, so this is only valid on block-level. return(false); } if (!IsCompoundStore(compoundStore, out var targetType, out var setterValue, context.TypeSystem)) { return(false); } // targetType = The type of the property/field/etc. being stored to. // setterValue = The value being stored. var storeInSetter = setterValue as StLoc; if (storeInSetter != null) { // We'll move the stloc to top-level: // callvirt set_Property(ldloc S_1, stloc v(binary.op(callvirt get_Property(ldloc S_1), value))) // ==> stloc v(compound.op.new(callvirt get_Property(ldloc S_1), value)) setterValue = storeInSetter.Value; if (storeInSetter.Variable.Type.IsSmallIntegerType()) { // 'stloc v' implicitly truncates the value. // Ensure that type of 'v' matches the type of the property: if (storeInSetter.Variable.Type.GetSize() != targetType.GetSize()) { return(false); } if (storeInSetter.Variable.Type.GetSign() != targetType.GetSign()) { return(false); } } } ILInstruction newInst; if (UnwrapSmallIntegerConv(setterValue, out var smallIntConv) is BinaryNumericInstruction binary) { if (compoundStore is StLoc) { // transform local variables only for user-defined operators return(false); } if (!IsMatchingCompoundLoad(binary.Left, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) { return(false); } if (!ValidateCompoundAssign(binary, smallIntConv, targetType)) { return(false); } context.Step($"Compound assignment (binary.numeric)", compoundStore); finalizeMatch?.Invoke(context); newInst = new NumericCompoundAssign( binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToNewValue); } else if (setterValue is Call operatorCall && operatorCall.Method.IsOperator) { if (operatorCall.Arguments.Count == 0) { return(false); } if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) { return(false); } ILInstruction rhs; if (operatorCall.Arguments.Count == 2) { if (CSharp.ExpressionBuilder.GetAssignmentOperatorTypeFromMetadataName(operatorCall.Method.Name) == null) { return(false); } rhs = operatorCall.Arguments[1]; } else if (operatorCall.Arguments.Count == 1) { if (!(operatorCall.Method.Name == "op_Increment" || operatorCall.Method.Name == "op_Decrement")) { return(false); } // use a dummy node so that we don't need a dedicated instruction for user-defined unary operator calls rhs = new LdcI4(1); } else { return(false); } if (operatorCall.IsLifted) { return(false); // TODO: add tests and think about whether nullables need special considerations } context.Step($"Compound assignment (user-defined binary)", compoundStore); finalizeMatch?.Invoke(context); newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToNewValue, target, targetKind, rhs); }
// convert from boolean to integer (or enum) return(new ConditionalExpression( this.Expression, LdcI4(compilation, 1).ConvertTo(targetType, expressionBuilder, checkForOverflow),