/// <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
                    });
                }
            }
        }
Exemple #2
0
        /// <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);
		}