/// <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); } }
/// <summary> /// Generates the code to serialize a reference field. /// </summary> private void SerializeReferenceField(StateSlotMetadata metadata) { // s = state _il.Emit(OpCodes.Ldloc_0); // *s = objs.GetObjectIdentifier(o.field) _il.Emit(OpCodes.Ldarg_0); PrepareFieldAccess(metadata); AccessField(metadata, OpCodes.Ldfld); _il.Emit(OpCodes.Call, _getObjectIdentifierMethod); _il.Emit(OpCodes.Stind_I2); }
/// <summary> /// Generates the code to serialize the state slot described by the <paramref name="metadata" />. /// </summary> /// <param name="metadata">The metadata of the state slot the code should be generated for.</param> private void SerializeField(StateSlotMetadata metadata) { LoadObject(metadata.ObjectIdentifier); if (IsReferenceType(metadata.DataType)) { SerializeReferenceField(metadata); } else { SerializePrimitiveTypeField(metadata); } }
/// <summary> /// Generates the code to deserialize a reference field. /// </summary> private void DeserializeReferenceField(StateSlotMetadata metadata) { // o = objs.GetObject(identifier) PrepareFieldAccess(metadata); // v = objs.GetObject(*state) _il.Emit(OpCodes.Ldarg_0); _il.Emit(OpCodes.Ldloc_0); _il.Emit(OpCodes.Ldind_I2); _il.Emit(OpCodes.Call, _getObjectMethod); // o.field = v AccessField(metadata, OpCodes.Stfld); }
/// <summary> /// Generates the code to deserialize the array state slot described by the <paramref name="metadata" />. /// </summary> /// <param name="metadata">The metadata of the state slot the code should be generated for.</param> private void DeserializeArray(StateSlotMetadata metadata) { var isReferenceType = metadata.DataType.IsReferenceType(); var loadCode = _bitLevelAddressing ? default(OpCode) : GetLoadElementOpCode(metadata.ElementSizeInBits / 8, metadata.EffectiveType.IsUnsignedNumericType()); var storeCode = isReferenceType ? OpCodes.Stelem_Ref : GetStoreArrayElementOpCode(GetUnmanagedSize(metadata.DataType)); LoadObject(metadata.ObjectIdentifier); for (var i = 0; i < metadata.ElementCount; ++i) { // o = &objs.GetObject(identifier)[i] _il.Emit(OpCodes.Ldloc_1); PrepareElementAccess(metadata, i); if (isReferenceType) { // v = objs.GetObject(*state) _il.Emit(OpCodes.Ldarg_0); _il.Emit(OpCodes.Ldloc_0); _il.Emit(OpCodes.Ldind_I2); _il.Emit(OpCodes.Call, _getObjectMethod); } else if (_bitLevelAddressing) { LoadBooleanValue(); } else { // v = *state _il.Emit(OpCodes.Ldloc_0); _il.Emit(loadCode); } // *o = v if (metadata.ContainedInStruct) { AccessField(metadata, OpCodes.Stfld); } else { _il.Emit(storeCode); } Advance(metadata.ElementSizeInBits / 8); } }
/// <summary> /// Prepares the access to the field referenced by the <paramref name="metadata" /> of an object. /// </summary> private void PrepareFieldAccess(StateSlotMetadata metadata) { _il.Emit(OpCodes.Ldloc_1); if (!metadata.ContainedInStruct) { return; } _il.Emit(OpCodes.Ldflda, metadata.Field); for (var i = 0; i < metadata.FieldChain.Length - 1; ++i) { _il.Emit(OpCodes.Ldflda, metadata.FieldChain[i]); } }
/// <summary> /// Prepares the access to the field referenced by the <paramref name="metadata" /> of an element contained in an array. /// </summary> private void PrepareElementAccess(StateSlotMetadata metadata, int elementIndex) { _il.Emit(OpCodes.Ldc_I4, elementIndex); if (!metadata.ContainedInStruct) { return; } _il.Emit(OpCodes.Ldelema, metadata.ObjectType.GetElementType()); for (var i = 0; i < metadata.FieldChain.Length - 1; ++i) { _il.Emit(OpCodes.Ldflda, metadata.FieldChain[i]); } }
/// <summary> /// Generates the code to deserialize the state slot described by the <paramref name="metadata" /> of the object stored in the /// local variable. /// </summary> /// <param name="metadata">The metadata of the state slot the code should be generated for.</param> private void DeserializePrimitiveTypeField(StateSlotMetadata metadata) { // o = objs.GetObject(identifier) PrepareFieldAccess(metadata); if (_bitLevelAddressing) { LoadBooleanValue(); } else { // v = *state _il.Emit(OpCodes.Ldloc_0); _il.Emit(GetLoadElementOpCode(metadata.ElementSizeInBits / 8, metadata.EffectiveType.IsUnsignedNumericType())); } // o.field = v AccessField(metadata, OpCodes.Stfld); }
/// <summary> /// Generates the code to serialize the state slot described by the <paramref name="metadata" /> of the object stored in the /// local variable. /// </summary> /// <param name="metadata">The metadata of the state slot the code should be generated for.</param> private void SerializePrimitiveTypeField(StateSlotMetadata metadata) { if (_bitLevelAddressing) { StoreBooleanValue(() => { PrepareFieldAccess(metadata); AccessField(metadata, OpCodes.Ldfld); }); return; } // s = state _il.Emit(OpCodes.Ldloc_0); // v = o.field PrepareFieldAccess(metadata); AccessField(metadata, OpCodes.Ldfld); // *s = v _il.Emit(GetStoreElementOpCode(metadata.ElementSizeInBits / 8)); }
/// <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> /// Generates the code to serialize the array state slot described by the <paramref name="metadata" />. /// </summary> /// <param name="metadata">The metadata of the state slot the code should be generated for.</param> private void SerializeArray(StateSlotMetadata metadata) { var isReferenceType = metadata.DataType.IsReferenceType(); var storeCode = _bitLevelAddressing ? default(OpCode) : GetStoreElementOpCode(metadata.ElementSizeInBits / 8); var loadCode = isReferenceType ? OpCodes.Ldelem_Ref : GetLoadArrayElementOpCode(GetUnmanagedSize(metadata.DataType), metadata.EffectiveType.IsUnsignedNumericType()); LoadObject(metadata.ObjectIdentifier); for (var i = 0; i < metadata.ElementCount; ++i) { if (_bitLevelAddressing) { StoreBooleanValue(() => { // o = objs.GetObject(identifier) _il.Emit(OpCodes.Ldloc_1); // v = o[i] PrepareElementAccess(metadata, i); if (metadata.ContainedInStruct) { AccessField(metadata, OpCodes.Ldfld); } else { _il.Emit(loadCode); } }); } else { // s = state _il.Emit(OpCodes.Ldloc_0); // o = objs.GetObject(identifier) if (isReferenceType) { _il.Emit(OpCodes.Ldarg_0); } _il.Emit(OpCodes.Ldloc_1); // v = o[i] PrepareElementAccess(metadata, i); if (metadata.ContainedInStruct) { AccessField(metadata, OpCodes.Ldfld); } else { _il.Emit(loadCode); } // v = objs.GetObjectIdentifier(o[i]) if (isReferenceType) { _il.Emit(OpCodes.Call, _getObjectIdentifierMethod); } // *s = v _il.Emit(storeCode); } Advance(metadata.ElementSizeInBits / 8); } }
/// <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); }