protected virtual void GetReadAndWriteMethods( CILTypeBase propertyType, out Action <CILField, MethodIL> read, out Action <CILField, MethodIL> read32, out Action <CILField, MethodIL> write, out Action <CILField, MethodIL> compareExchange, out CILTypeBase fieldType ) { fieldType = propertyType; // Boolean isBooleanProperty = BOOLEAN_TYPE.Equals( propertyType ); // We must ask whether type is value type before asking whether it is enum, because type builder proxies throw if asked directly if it is enum. Action <CILTypeBase, CILTypeBase, MethodIL> fieldToPropertyCast = null; Action <CILTypeBase, CILTypeBase, MethodIL> propertyToFieldCast = null; CILMethod iExchangeMethod; CILMethod iCompareExchangeMethod; var tc = propertyType.GetTypeCode(CILTypeCode.Empty); switch (tc) { case CILTypeCode.Object: case CILTypeCode.SystemObject: case CILTypeCode.Type: case CILTypeCode.String: case CILTypeCode.DateTime: case CILTypeCode.Decimal: case CILTypeCode.Empty: case CILTypeCode.IntPtr: case CILTypeCode.UIntPtr: var isObject = tc == CILTypeCode.SystemObject; var isIntPtr = tc == CILTypeCode.IntPtr; // TODO if generic parameter has same constraint as Interlocked.Exchange<T>, we can use the Interlocked.Exchange<T> directly. if (propertyType.IsGenericParameter() || (propertyType.IsValueType() && /*&& !propertyType.IsInterface() && !isObject*/ (this.isSilverLight || !isIntPtr) )) { // TODO if value type => maybe use placeholder class? (Like System.Lazy<T> does) fieldType = OBJECT_TYPE; } // SL doesn't have IntPtr or Object Interlocked.Exchange methods iExchangeMethod = (this.isSilverLight || (!isIntPtr && !isObject)) ? INTERLOCKED_EXCHANGE_METHOD_GDEF.MakeGenericMethod(fieldType) : (isIntPtr ? INTERLOCKED_EXCHANGE_INT_PTR_METHOD : INTERLOCKED_EXCHANGE_OBJECT_METHOD); // SL doesn't have IntPtr Interlocked.CompareExchange method (but oddly enough has the Object variation) iCompareExchangeMethod = isObject ? INTERLOCKED_COMPARE_EXCHANGE_OBJECT_METHOD : ((this.isSilverLight || !isIntPtr) ? INTERLOCKED_COMPARE_EXCHANGE_METHOD_GDEF.MakeGenericMethod(fieldType) : INTERLOCKED_COMPARE_EXCHANGE_INT_PTR_METHOD); fieldToPropertyCast = (fType, pType, il) => il.EmitCastToType(fType, pType); propertyToFieldCast = (fType, pType, il) => il.EmitCastToType(pType, fType); break; case CILTypeCode.Int64: case CILTypeCode.UInt64: fieldType = INT64_TYPE; iExchangeMethod = INTERLOCKED_EXCHANGE_I64_METHOD; iCompareExchangeMethod = INTERLOCKED_COMPARE_EXCHANGE_I64_METHOD; break; case CILTypeCode.Boolean: fieldType = INT32_TYPE; iExchangeMethod = INTERLOCKED_EXCHANGE_I32_METHOD; iCompareExchangeMethod = INTERLOCKED_COMPARE_EXCHANGE_I32_METHOD; fieldToPropertyCast = (fType, pType, il) => il.EmitLoadInt32(0).EmitCeq().EmitLoadInt32(0).EmitCeq(); propertyToFieldCast = (fType, pType, il) => { var isTrueLabelWrapper = il.DefineLabel(); var afterConversionLabelWrapper = il.DefineLabel(); il.EmitBranch(BranchType.IF_TRUE, isTrueLabelWrapper); il.EmitLoadInt32(0); il.EmitBranch(BranchType.ALWAYS, afterConversionLabelWrapper); il.MarkLabel(isTrueLabelWrapper); il.EmitLoadInt32(1); il.MarkLabel(afterConversionLabelWrapper); }; break; case CILTypeCode.Byte: case CILTypeCode.Char: case CILTypeCode.Int16: case CILTypeCode.Int32: case CILTypeCode.SByte: case CILTypeCode.UInt16: case CILTypeCode.UInt32: fieldType = INT32_TYPE; iExchangeMethod = INTERLOCKED_EXCHANGE_I32_METHOD; iCompareExchangeMethod = INTERLOCKED_COMPARE_EXCHANGE_I32_METHOD; break; case CILTypeCode.Double: iExchangeMethod = this.isSilverLight ? INTERLOCKED_EXCHANGE_I64_METHOD : INTERLOCKED_EXCHANGE_R64_METHOD; iCompareExchangeMethod = this.isSilverLight ? INTERLOCKED_COMPARE_EXCHANGE_I64_METHOD : INTERLOCKED_COMPARE_EXCHANGE_R64_METHOD; fieldType = this.isSilverLight ? INT64_TYPE : DOUBLE_TYPE; if (this.isSilverLight) { fieldToPropertyCast = (fType, pType, il) => il.EmitCall(INT64_BITS_TO_DOUBLE); propertyToFieldCast = (fType, pType, il) => il.EmitCall(DOUBLE_BITS_TO_INT64); } break; case CILTypeCode.Single: iExchangeMethod = this.isSilverLight ? INTERLOCKED_EXCHANGE_I32_METHOD : INTERLOCKED_EXCHANGE_R32_METHOD; iCompareExchangeMethod = this.isSilverLight ? INTERLOCKED_COMPARE_EXCHANGE_I32_METHOD : INTERLOCKED_COMPARE_EXCHANGE_R32_METHOD; fieldType = this.isSilverLight ? INT32_TYPE : SINGLE_TYPE; if (this.isSilverLight) { // TODO this is slow // Field to property -> BitConverter.ToInt32(BitConverter.GetBytes(<field>), 0); // Property to field -> BitConverter.ToSingle(BitConverter.GetBytes(<property>), 0); fieldToPropertyCast = (fType, pType, il) => il.EmitCall(GET_BYTES_INT32).EmitLoadInt32(0).EmitCall(BYTES_TO_SINGLE); propertyToFieldCast = (fType, pType, il) => il.EmitCall(GET_BYTES_SINGLE).EmitLoadInt32(0).EmitCall(BYTES_TO_INT32); } break; default: throw new ArgumentException("Invalid type code: " + tc); } if (fieldToPropertyCast == null) { fieldToPropertyCast = (fType, pType, il) => il.EmitNumericConversion(fType, pType, false); } if (propertyToFieldCast == null) { propertyToFieldCast = (fType, pType, il) => il.EmitNumericConversion(pType, fType, false); } var fieldTypeActual = fieldType; read = new Action <CILField, MethodIL>((field, il) => { // just load this field il.EmitLoadThisField(field); fieldToPropertyCast(fieldTypeActual, propertyType, il); }); read32 = !this.isSilverLight && CILTypeCode.Int64 == fieldType.GetTypeCode(CILTypeCode.Empty) ? new Action <CILField, MethodIL>((field, il) => { // Interlocked.Read(ref this.<field>); il .EmitLoadThisFieldAddress(field) .EmitCall(INTERLOCKED_READ_I64_METHOD); fieldToPropertyCast(fieldTypeActual, propertyType, il); }) : null; write = (field, il) => { // Interlocked.Exchange(ref this.<field>, (<field type>)value); il .EmitLoadThisFieldAddress(field) .EmitLoadArg(1); propertyToFieldCast(fieldTypeActual, propertyType, il); il.EmitCall(iExchangeMethod); }; compareExchange = (field, il) => { // Interlocked.CompareExchange(ref this._property, <arg-2>, <arg-1>); il .EmitLoadThisFieldAddress(field) .EmitLoadArg(2); propertyToFieldCast(fieldTypeActual, propertyType, il); il.EmitLoadArg(1); propertyToFieldCast(fieldTypeActual, propertyType, il); il.EmitCall(iCompareExchangeMethod); fieldToPropertyCast(fieldTypeActual, propertyType, il); }; }
protected virtual void EmitEventRelatedThings( CompositeTypeGenerationInfo thisGenerationInfo, CompositeMethodGenerationInfo thisMethodGenerationInfo, EventModel eventModel, Type genericEventMixinType ) { var nEventInfo = eventModel.NativeInfo; var eventInfo = nEventInfo.NewWrapper(this.ctx); CompositeMethodGenerationInfo invokeMethod = null; if (this.IsCompositeGeneratedEvent(thisGenerationInfo, eventModel, genericEventMixinType)) { var eventIdx = thisGenerationInfo.AutoGeneratedEventInfos.Count; var eventType = TypeGenerationUtils.CreateTypeForEmitting(eventInfo.EventHandlerType, thisGenerationInfo.GenericArguments, null); var invokeMethodFromModel = eventModel.NativeInfo.EventHandlerType.GetMethod(DELEGATE_INVOKE_METHOD_NAME).NewWrapper(this.ctx); CILTypeBase fieldType; Action <CILField, MethodIL> addAction, removeAction; Action <EventModel, CompositeTypeGenerationInfo, CILField, CILTypeBase, CompositeMethodGenerationInfo, CILMethod> invokeAction; Action <CILField, MethodIL> checkAction; this.CreateEmittingActionsForEvent( eventModel, eventType, out fieldType, out addAction, out removeAction, out invokeAction, out checkAction ); // Field: // private <field type> _event<idx>; var eventField = thisGenerationInfo.Builder.AddField( EVENT_FIELD_PREFIX + eventIdx, fieldType, FieldAttributes.Private ); var addMethod = this.ImplementMethodForEmitting( thisGenerationInfo, eventModel.AddMethod.NativeInfo.NewWrapper(this.ctx), EVENT_METHOD_PREFIX + eventIdx + EVENT_ADDITION_POSTFIX, MethodAttributes.Private | MethodAttributes.HideBySig, false ); this.EmitThrowIfApplicationNotActiveWithoutLocalVariable(thisGenerationInfo, addMethod.IL); addAction(eventField, addMethod.IL); CompositeMethodGenerationInfo removeMethod = this.ImplementMethodForEmitting( thisGenerationInfo, eventModel.RemoveMethod.NativeInfo.NewWrapper(this.ctx), EVENT_METHOD_PREFIX + eventIdx + EVENT_REMOVAL_POSTFIX, MethodAttributes.Private | MethodAttributes.HideBySig, false ); this.EmitThrowIfApplicationNotActiveWithoutLocalVariable(thisGenerationInfo, removeMethod.IL); removeAction(eventField, removeMethod.IL); invokeMethod = this.ImplementMethodForEmitting( thisGenerationInfo, decType => TypeGenerationUtils.CreateTypeForEmittingCILType(decType, thisGenerationInfo.GenericArguments, null), invokeMethodFromModel, EVENT_METHOD_PREFIX + eventIdx + EVENT_INVOCATION_POSTFIX, MethodAttributes.Private | MethodAttributes.HideBySig, false); var eventMethodToInvoke = invokeMethod.OverriddenMethod; this.EmitThrowIfApplicationNotActiveWithoutLocalVariable(thisGenerationInfo, invokeMethod.IL); invokeAction(eventModel, thisGenerationInfo, eventField, eventType, invokeMethod, eventMethodToInvoke); // Field clearer method: // private void Event<idx>Clear() // { // Interlocked.Exchange<TEvent>(ref this._event<idx>, null); // } var eventClearMB = thisGenerationInfo.Builder.AddMethod( EVENT_METHOD_PREFIX + eventIdx + EVENT_CLEAR_POSTFIX, MethodAttributes.Private | MethodAttributes.HideBySig, CallingConventions.HasThis); var il = eventClearMB.MethodIL; this.EmitThrowIfApplicationNotActiveWithoutLocalVariable(thisGenerationInfo, il); il.EmitLoadThisFieldAddress(eventField); il.EmitLoadNull(); il.EmitCall(INTERLOCKED_EXCHANGE_METHOD_GDEF.MakeGenericMethod(fieldType)); il.EmitPop(); il.EmitReturn(); // Field checker method: // private Boolean Event<idx>Checker() // { // // for strong refs // return this._event<idx>; // // for weak refs // return // } var eventCheckerMB = thisGenerationInfo.Builder.AddMethod( EVENT_METHOD_PREFIX + eventIdx + EVENT_CHECKER_POSTFIX, MethodAttributes.Private | MethodAttributes.HideBySig, CallingConventions.HasThis ); eventCheckerMB.ReturnParameter.ParameterType = BOOLEAN_TYPE; il = eventCheckerMB.MethodIL; this.EmitThrowIfApplicationNotActiveWithoutLocalVariable(thisGenerationInfo, il); checkAction(eventField, il); thisGenerationInfo.AutoGeneratedEventInfos.Add( nEventInfo, new EventGenerationInfo( this.EmitRefMethodForPropertyOrEvent(eventField, EVENT_METHOD_PREFIX + eventIdx + REF_INVOKER_METHOD_SUFFIX), eventModel, eventType, eventField, addMethod.Builder, removeMethod.Builder, invokeMethod.Builder, eventClearMB, eventCheckerMB ) ); } if (this.NeedToEmitAdditionalMemberInfo(thisGenerationInfo, eventInfo.Name, (parent, name) => parent.DeclaredEvents.FirstOrDefault(evt => Object.Equals(evt.Name, name)))) { // Need to define event if we inherit property directly from interface var name = eventInfo.Name; if (thisGenerationInfo.RawPropertyInfos.Keys.Any(pInfo => pInfo.Name.Equals(name))) { // We already have event with the same name from different type name = QualifiedName.FromMemberInfo(nEventInfo).ToString(); } CILEvent eBuilder; if (!thisGenerationInfo.RawEventInfos.TryGetValue(nEventInfo, out eBuilder)) { eBuilder = thisGenerationInfo.Builder.AddEvent( name, eventInfo.Attributes, TypeGenerationUtils.CreateTypeForEmitting(eventInfo.EventHandlerType, thisGenerationInfo.GenericArguments, null) ); if (invokeMethod != null) { eBuilder.RaiseMethod = invokeMethod.Builder; } thisGenerationInfo.RawEventInfos.Add(nEventInfo, eBuilder); } if (thisMethodGenerationInfo.MethodFromModel.Equals(eventInfo.AddMethod)) { eBuilder.AddMethod = thisMethodGenerationInfo.Builder; } else if (thisMethodGenerationInfo.MethodFromModel.Equals(eventInfo.RemoveMethod)) { eBuilder.RemoveMethod = thisMethodGenerationInfo.Builder; } else { throw new InternalException("Found an event, but neither adder nor remover methods matched the method being emitted. Event is " + eventInfo + ", method is " + thisMethodGenerationInfo.MethodFromModel + "."); } } }