/// <summary> /// Generates the state slot metadata for the <paramref name="obj" />. /// </summary> /// <param name="obj">The object the state slot metadata should be generated for.</param> /// <param name="objectIdentifier">The identifier of the <paramref name="obj" />.</param> /// <param name="mode">The serialization mode that should be used to generate the metadata.</param> protected internal override IEnumerable <StateSlotMetadata> GetStateSlotMetadata(object obj, int objectIdentifier, SerializationMode mode) { foreach (var field in GetFields(obj, mode)) { if (field.FieldType.IsStructType()) { foreach (var metadataSlot in StateSlotMetadata.FromStruct(field.FieldType, mode)) { metadataSlot.Object = obj; metadataSlot.ObjectIdentifier = objectIdentifier; metadataSlot.ObjectType = obj.GetType(); metadataSlot.ElementCount = 1; metadataSlot.Field = field; yield return(metadataSlot); } } else { yield return(new StateSlotMetadata { Object = obj, ObjectType = obj.GetType(), ObjectIdentifier = objectIdentifier, DataType = field.FieldType, Field = field, ElementCount = 1 }); } } }
/// <summary> /// Generates the state slot metadata for the <paramref name="obj" />. /// </summary> /// <param name="obj">The object the state slot metadata should be generated for.</param> /// <param name="objectIdentifier">The identifier of the <paramref name="obj" />.</param> /// <param name="mode">The serialization mode that should be used to generate the metadata.</param> protected internal override IEnumerable <StateSlotMetadata> GetStateSlotMetadata(object obj, int objectIdentifier, SerializationMode mode) { Requires.That(((Array)obj).Rank == 1 && !obj.GetType().GetElementType().IsArray, "Multidimensional arrays are not supported."); var elementType = obj.GetType().GetElementType(); var length = ((Array)obj).GetLength(0); if (elementType.IsStructType()) { foreach (var metadataSlot in StateSlotMetadata.FromStruct(elementType, mode)) { metadataSlot.Object = obj; metadataSlot.ObjectIdentifier = objectIdentifier; metadataSlot.ObjectType = obj.GetType(); metadataSlot.ElementCount = length; yield return(metadataSlot); } } else { yield return(new StateSlotMetadata { Object = obj, ObjectIdentifier = objectIdentifier, ObjectType = obj.GetType(), DataType = obj.GetType().GetElementType(), ElementCount = length }); } }
/// <summary> /// Accesses the field on the object currently on the stack. /// </summary> private void AccessField(StateSlotMetadata metadata, OpCode accessCode) { var field = metadata.ContainedInStruct ? metadata.FieldChain.Last() : metadata.Field; _il.Emit(accessCode, field); }
/// <summary> /// Prepares the access to the field referenced by the <paramref name="metadata" />. /// </summary> private void PrepareAccess(StateSlotMetadata metadata, int elementIndex) { _il.Emit(OpCodes.Ldloc_0); if (!metadata.ContainedInStruct) return; if (metadata.ObjectType.IsArray) { _il.Emit(OpCodes.Ldc_I4, elementIndex); _il.Emit(OpCodes.Ldelema, metadata.ObjectType.GetElementType()); } else _il.Emit(OpCodes.Ldflda, metadata.Field); for (var i = 0; i < metadata.FieldChain.Length - 1; ++i) _il.Emit(OpCodes.Ldflda, metadata.FieldChain[i]); }
/// <summary> /// Generates the code to restrict the values contained in ranged object fields. /// </summary> private void RestrictField(StateSlotMetadata metadata, int elementIndex = 0) { var exceedsFourBytes = metadata.ElementSizeInBits > 32; var continueLabel = _il.DefineLabel(); switch (metadata.Range.OverflowBehavior) { case OverflowBehavior.Error: // if (v < lower | v > upper) throw PrepareAccess(metadata, elementIndex); AccessField(metadata, OpCodes.Ldfld); LoadConstant(metadata.Range.LowerBound, exceedsFourBytes); _il.Emit(metadata.DataType.IsUnsignedNumericType() ? OpCodes.Clt_Un : OpCodes.Clt); PrepareAccess(metadata, elementIndex); AccessField(metadata, OpCodes.Ldfld); LoadConstant(metadata.Range.UpperBound, exceedsFourBytes); _il.Emit(metadata.DataType.IsUnsignedNumericType() ? OpCodes.Cgt_Un : OpCodes.Cgt); _il.Emit(OpCodes.Or); _il.Emit(OpCodes.Brfalse, continueLabel); // throw new RangeViolationException(obj, field) _il.Emit(OpCodes.Ldloc_0); var field = metadata.ContainedInStruct ? metadata.FieldChain.Last() : metadata.Field; _il.Emit(OpCodes.Ldtoken, field); _il.Emit(OpCodes.Ldtoken, field.DeclaringType); var parameters = new[] { typeof(RuntimeFieldHandle), typeof(RuntimeTypeHandle) }; _il.Emit(OpCodes.Call, typeof(FieldInfo).GetMethod("GetFieldFromHandle", parameters)); LoadConstant(metadata.Range.LowerBound, exceedsFourBytes); _il.Emit(OpCodes.Box, metadata.EffectiveType); LoadConstant(metadata.Range.UpperBound, exceedsFourBytes); _il.Emit(OpCodes.Box, metadata.EffectiveType); _il.Emit(OpCodes.Ldc_I4, (int)OverflowBehavior.Error); _il.Emit(OpCodes.Newobj, typeof(RangeAttribute).GetConstructors().Single()); _il.Emit(OpCodes.Newobj, typeof(RangeViolationException).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Single()); _il.Emit(OpCodes.Throw); break; case OverflowBehavior.Clamp: var clampLabel = _il.DefineLabel(); PrepareAccess(metadata, elementIndex); AccessField(metadata, OpCodes.Ldfld); _il.Emit(OpCodes.Dup); // if (v < lower) v = lower LoadConstant(metadata.Range.LowerBound, exceedsFourBytes); _il.Emit(metadata.DataType.IsUnsignedNumericType() ? OpCodes.Bge_Un_S : OpCodes.Bge_S, clampLabel); _il.Emit(OpCodes.Pop); PrepareAccess(metadata, elementIndex); LoadConstant(metadata.Range.LowerBound, exceedsFourBytes); AccessField(metadata, OpCodes.Stfld); _il.Emit(OpCodes.Br, continueLabel); // else if (v > upper) v = upper _il.MarkLabel(clampLabel); LoadConstant(metadata.Range.UpperBound, exceedsFourBytes); _il.Emit(metadata.DataType.IsUnsignedNumericType() ? OpCodes.Ble_Un_S : OpCodes.Ble_S, continueLabel); PrepareAccess(metadata, elementIndex); LoadConstant(metadata.Range.UpperBound, exceedsFourBytes); AccessField(metadata, OpCodes.Stfld); break; case OverflowBehavior.WrapClamp: var wrapLabel = _il.DefineLabel(); PrepareAccess(metadata, elementIndex); AccessField(metadata, OpCodes.Ldfld); _il.Emit(OpCodes.Dup); // if (v < lower) v = upper LoadConstant(metadata.Range.LowerBound, exceedsFourBytes); _il.Emit(metadata.DataType.IsUnsignedNumericType() ? OpCodes.Bge_Un_S : OpCodes.Bge_S, wrapLabel); _il.Emit(OpCodes.Pop); PrepareAccess(metadata, elementIndex); LoadConstant(metadata.Range.UpperBound, exceedsFourBytes); AccessField(metadata, OpCodes.Stfld); _il.Emit(OpCodes.Br, continueLabel); // else if (v > upper) v = lower _il.MarkLabel(wrapLabel); LoadConstant(metadata.Range.UpperBound, exceedsFourBytes); _il.Emit(metadata.DataType.IsUnsignedNumericType() ? OpCodes.Ble_Un_S : OpCodes.Ble_S, continueLabel); PrepareAccess(metadata, elementIndex); LoadConstant(metadata.Range.LowerBound, exceedsFourBytes); AccessField(metadata, OpCodes.Stfld); break; default: Assert.NotReached("Unknown overflow behavior."); break; } _il.MarkLabel(continueLabel); }
/// <summary> /// Generates the code to restrict the values contained in ranged array fields. /// </summary> private void RestrictFields(StateSlotMetadata metadata) { for (var i = 0; i < metadata.ElementCount; ++i) RestrictField(metadata, i); }