/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, AssemblyCompiler compiler) { // Optimize enum2int(ldsfld(enum-const)) foreach (var node in ast.GetExpressions()) { switch (node.Code) { case AstCode.Enum_to_int: case AstCode.Enum_to_long: { var arg = node.Arguments[0]; XFieldReference fieldRef; if (arg.Match(AstCode.Ldsfld, out fieldRef)) { XFieldDefinition field; object value; if (fieldRef.TryResolve(out field) && field.IsStatic && field.DeclaringType.IsEnum && field.TryGetEnumValue(out value)) { // Replace with ldc_ix var wide = (node.Code == AstCode.Enum_to_long); node.SetCode(wide ? AstCode.Ldc_I8 : AstCode.Ldc_I4); node.Operand = wide ? (object)XConvert.ToLong(value) : XConvert.ToInt(value); node.Arguments.Clear(); } } } break; } } }
/// <summary> /// Set the value of the given dex field. /// </summary> protected virtual void SetFieldValue(DexLib.FieldDefinition dfield, FieldDefinition field) { var constant = field.Constant; if (constant != null) { var fieldType = field.FieldType; if (fieldType.IsByte()) { constant = XConvert.ToByte(constant); } else if (fieldType.IsUInt16()) { constant = XConvert.ToShort(constant); } else if (fieldType.IsUInt32()) { constant = XConvert.ToInt(constant); } else if (fieldType.IsUInt64()) { constant = XConvert.ToLong(constant); } } dfield.Value = constant; }
/// <summary> /// Generate converter for the value of an Const instruction. /// </summary> internal static Func <object, object> ConstValueConverter(this XTypeReference elementType, bool keepUnsigned) { if (elementType.IsBoolean()) { return(x => Convert.ToBoolean(x) ? 1 : 0); } if (elementType.IsByte()) { if (keepUnsigned) { return(x => (int)Convert.ToByte(x)); } return(x => (int)((sbyte)unchecked (Convert.ToByte(x)))); } if (elementType.IsSByte()) { return(x => (int)(Convert.ToSByte(x))); } if (elementType.IsChar()) { return(x => (int)(Convert.ToChar(x))); } if (elementType.IsUInt16()) { return(x => (int)(Convert.ToUInt16(x))); } if (elementType.IsInt16()) { return(x => (int)(Convert.ToInt16(x))); } if (elementType.IsInt32()) { return(x => XConvert.ToInt(x)); } if (elementType.IsUInt32()) { return(x => XConvert.ToInt(x)); // unchecked((int)Convert.ToUInt32(Convert.ToInt64(x) & 0xFFFFFFFF)); } if (elementType.IsFloat()) { return(x => Convert.ToSingle(x)); } if (elementType.IsInt64()) { return(x => XConvert.ToLong(x)); } if (elementType.IsUInt64()) { return(x => XConvert.ToLong(x)); // unchecked((long)Convert.ToInt64(x)); } if (elementType.IsDouble()) { return(x => Convert.ToDouble(x)); } if (elementType.IsDexObject() && !elementType.IsVoid()) { return(x => x); } throw new NotSupportedException("Unknown type for constValueConverter " + elementType); }
/// <summary> /// Create the body of the class ctor. /// </summary> private AstBlock CreateClassCtorBody(bool isWide, XFieldDefinition enumInfoField, XFieldDefinition defaultField, XMethodReference enumInfoCtor, XTypeReference valueType, XTypeSystem typeSystem) { var internalEnumType = Compiler.GetDot42InternalType("Enum"); var internalEnumInfoType = Compiler.GetDot42InternalType("EnumInfo"); var valueToFieldMap = new Dictionary <object, XFieldDefinition>(); var ldc = isWide ? AstCode.Ldc_I8 : AstCode.Ldc_I4; var ast = AstBlock.CreateOptimizedForTarget( // Instantiate enum info field new AstExpression(AstNode.NoSource, AstCode.Stsfld, enumInfoField, new AstExpression(AstNode.NoSource, AstCode.Newobj, enumInfoCtor))); // Instantiate values for each field var ordinal = 0; foreach (var field in XType.Fields.Where(x => x.IsStatic && !(x is XSyntheticFieldDefinition))) { // Find dex field object value; if (!field.TryGetEnumValue(out value)) { throw new CompilerException(string.Format("Cannot get enum value from field {0}", field.FullName)); } value = isWide ? (object)XConvert.ToLong(value) : (object)XConvert.ToInt(value); XFieldDefinition existingField; AstExpression valueExpr; if (valueToFieldMap.TryGetValue(value, out existingField)) { // Re-use instance of existing field valueExpr = new AstExpression(AstNode.NoSource, AstCode.Ldsfld, existingField); } else { // Record valueToFieldMap[value] = field; // Call ctor valueExpr = new AstExpression(AstNode.NoSource, AstCode.Newobj, ctor, new AstExpression(AstNode.NoSource, AstCode.Ldstr, field.Name), new AstExpression(AstNode.NoSource, AstCode.Ldc_I4, ordinal), new AstExpression(AstNode.NoSource, ldc, value)); } // Initialize static field ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Stsfld, field, valueExpr)); // Add to info var addMethod = new XMethodReference.Simple("Add", true, typeSystem.Void, internalEnumInfoType, XParameter.Create("value", valueType), XParameter.Create("instance", internalEnumType)); ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Call, addMethod, new AstExpression(AstNode.NoSource, AstCode.Ldsfld, enumInfoField), new AstExpression(AstNode.NoSource, ldc, value), new AstExpression(AstNode.NoSource, AstCode.Ldsfld, field))); // Increment ordinal ordinal++; } // Initialize default field var getValueMethod = new XMethodReference.Simple("GetValue", true, internalEnumType, internalEnumInfoType, XParameter.Create("value", valueType)); ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Stsfld, defaultField, new AstExpression(AstNode.NoSource, AstCode.SimpleCastclass, XType, new AstExpression(AstNode.NoSource, AstCode.Call, getValueMethod, new AstExpression(AstNode.NoSource, AstCode.Ldsfld, enumInfoField), new AstExpression(AstNode.NoSource, ldc, 0))))); // Return ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Ret, null)); return(ast); }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, AssemblyCompiler compiler) { foreach (var node in ast.GetExpressions()) { switch (node.Code) { // Optimize enum2int(ldsfld(enum-const)) // and enum2int(int.to.enum(xxx)) case AstCode.Enum_to_int: case AstCode.Enum_to_long: { var arg = node.Arguments[0]; XFieldReference fieldRef; if (arg.Match(AstCode.Ldsfld, out fieldRef)) { XFieldDefinition field; object value; if (fieldRef.TryResolve(out field) && field.IsStatic && field.DeclaringType.IsEnum && field.TryGetEnumValue(out value)) { // Replace with ldc_ix var wide = (node.Code == AstCode.Enum_to_long); node.SetCode(wide ? AstCode.Ldc_I8 : AstCode.Ldc_I4); node.Operand = wide ? (object)XConvert.ToLong(value) : XConvert.ToInt(value); node.Arguments.Clear(); } } else if (arg.Code == AstCode.Int_to_enum || arg.Code == AstCode.Long_to_enum) { var expectedType = node.ExpectedType; node.CopyFrom(arg.Arguments[0]); node.ExpectedType = expectedType; } } break; // optimize ceq/cne (int/long-to-enum(int), yyy) case AstCode.Ceq: case AstCode.Cne: { if (node.Arguments.Any(a => IsToEnum(a.Code))) { // xx_to_enum is a quite costly operation when compared to enum-to-int, // so convert this to an interger-only compare. bool isLong = node.Arguments.Any(a => a.Code == AstCode.Long_to_enum); foreach (var arg in node.Arguments) { if (IsToEnum(arg.Code)) { arg.CopyFrom(arg.Arguments[0]); arg.ExpectedType = isLong ? compiler.Module.TypeSystem.Long : compiler.Module.TypeSystem.Int; } else { Debug.Assert(arg.GetResultType().IsEnum()); var orig = new AstExpression(arg); var convert = new AstExpression(arg.SourceLocation, isLong ? AstCode.Enum_to_long : AstCode.Enum_to_int, null, orig); convert.ExpectedType = isLong ? compiler.Module.TypeSystem.Long : compiler.Module.TypeSystem.Int; arg.CopyFrom(convert); } } } break; } } } }