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);
            };
        }
コード例 #2
0
        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 + ".");
                }
            }
        }