/// <summary> /// Introduce a named argument for 'arg' and evaluate it before the other arguments /// (except for the "this" pointer) /// </summary> internal static void IntroduceNamedArgument(ILInstruction arg, ILTransformContext context) { var call = (CallInstruction)arg.Parent; Debug.Assert(context.Function == call.Ancestors.OfType <ILFunction>().First()); var v = context.Function.RegisterVariable(VariableKind.NamedArgument, arg.InferType(context.TypeSystem)); context.Step($"Introduce named argument '{v.Name}'", arg); if (!(call.Parent is Block namedArgBlock) || namedArgBlock.Kind != BlockKind.CallWithNamedArgs) { // create namedArgBlock: namedArgBlock = new Block(BlockKind.CallWithNamedArgs); call.ReplaceWith(namedArgBlock); namedArgBlock.FinalInstruction = call; if (call.IsInstanceCall) { IType thisVarType = call.Method.DeclaringType; if (CallInstruction.ExpectedTypeForThisPointer(thisVarType) == StackType.Ref) { thisVarType = new ByReferenceType(thisVarType); } var thisArgVar = context.Function.RegisterVariable(VariableKind.NamedArgument, thisVarType, "this_arg"); namedArgBlock.Instructions.Add(new StLoc(thisArgVar, call.Arguments[0])); call.Arguments[0] = new LdLoc(thisArgVar); } } int argIndex = arg.ChildIndex; Debug.Assert(call.Arguments[argIndex] == arg); namedArgBlock.Instructions.Insert(call.IsInstanceCall ? 1 : 0, new StLoc(v, arg)); call.Arguments[argIndex] = new LdLoc(v); }
/// <summary> /// Gets whether 'stobj type(..., value)' would evaluate to a different value than 'value' /// due to implicit truncation. /// </summary> static internal bool IsImplicitTruncation(ILInstruction value, IType type, bool allowNullableValue = false) { if (!type.IsSmallIntegerType()) { // Implicit truncation in ILAst only happens for small integer types; // other types of implicit truncation in IL cause the ILReader to insert // conv instructions. return(false); } // With small integer types, test whether the value might be changed by // truncation (based on type.GetSize()) followed by sign/zero extension (based on type.GetSign()). // (it's OK to have false-positives here if we're unsure) if (value.MatchLdcI4(out int val)) { switch (type.GetEnumUnderlyingType().GetDefinition()?.KnownTypeCode) { case KnownTypeCode.Boolean: return(!(val == 0 || val == 1)); case KnownTypeCode.Byte: return(!(val >= byte.MinValue && val <= byte.MaxValue)); case KnownTypeCode.SByte: return(!(val >= sbyte.MinValue && val <= sbyte.MaxValue)); case KnownTypeCode.Int16: return(!(val >= short.MinValue && val <= short.MaxValue)); case KnownTypeCode.UInt16: case KnownTypeCode.Char: return(!(val >= ushort.MinValue && val <= ushort.MaxValue)); } } else if (value is Conv conv) { return(conv.TargetType != type.ToPrimitiveType()); } else if (value is Comp) { return(false); // comp returns 0 or 1, which always fits } else if (value is IfInstruction ifInst) { return(IsImplicitTruncation(ifInst.TrueInst, type, allowNullableValue) || IsImplicitTruncation(ifInst.FalseInst, type, allowNullableValue)); } else { IType inferredType = value.InferType(); if (allowNullableValue) { inferredType = NullableType.GetUnderlyingType(inferredType); } if (inferredType.Kind != TypeKind.Unknown) { return(!(inferredType.GetSize() <= type.GetSize() && inferredType.GetSign() == type.GetSign())); } } return(true); }
internal static IType GuessType(IType variableType, ILInstruction inst, ILTransformContext context) { if (!variableType.IsKnownType(KnownTypeCode.Object)) return variableType; IType inferredType = inst.InferType(context.TypeSystem); if (inferredType.Kind != TypeKind.Unknown) return inferredType; else return variableType; }
internal static IType GuessType(IType variableType, ILInstruction inst, ILTransformContext context) { if (!variableType.IsKnownType(KnownTypeCode.Object)) { return(variableType); } IType inferredType = inst.InferType(); if (inferredType.Kind != TypeKind.Unknown) { return(inferredType); } else { return(variableType); } }