예제 #1
0
        public void AddTargetTypeTest(object target, RubyClass/*!*/ targetClass, Expression/*!*/ targetParameter, DynamicMetaObject/*!*/ metaContext,
            IEnumerable<string>/*!*/ resolvedNames) {

            // no changes to the module's class hierarchy while building the test:
            targetClass.Context.RequiresClassHierarchyLock();

            // initialization changes the version number, so ensure that the module is initialized:
            targetClass.InitializeMethodsNoLock();

            var context = (RubyContext)metaContext.Value;

            if (target is IRubyObject) {
                Type type = target.GetType();
                AddTypeRestriction(type, targetParameter);
            
                // Ruby objects (get the method directly to prevent interface dispatch):
                MethodInfo classGetter = type.GetMethod(Methods.IRubyObject_get_ImmediateClass.Name, BindingFlags.Public | BindingFlags.Instance);
                if (type.IsVisible && classGetter != null && classGetter.ReturnType == typeof(RubyClass)) {
                    AddCondition(
                        // (#{type})target.ImmediateClass.Version.Method == #{immediateClass.Version.Method}
                        Ast.Equal(
                            Ast.Field(
                                Ast.Field(
                                    Ast.Call(Ast.Convert(targetParameter, type), classGetter), 
                                    Fields.RubyModule_Version
                                ),
                                Fields.VersionHandle_Method
                            ),
                            AstUtils.Constant(targetClass.Version.Method)
                        )
                    );
                    return;
                }

                // TODO: explicit iface-implementation
                throw new NotSupportedException("Type implementing IRubyObject should be visible and have ImmediateClass getter");
            }

            AddRuntimeTest(metaContext);

            // singleton nil:
            if (target == null) {
                AddRestriction(Ast.Equal(targetParameter, AstUtils.Constant(null)));
                AddVersionTest(context.NilClass);
                return;
            }

            // singletons true, false:
            if (target is bool) {
                AddRestriction(Ast.AndAlso(
                    Ast.TypeIs(targetParameter, typeof(bool)),
                    Ast.Equal(Ast.Convert(targetParameter, typeof(bool)), AstUtils.Constant(target))
                ));

                AddVersionTest((bool)target ? context.TrueClass : context.FalseClass);
                return;
            }

            var nominalClass = targetClass.NominalClass;

            Debug.Assert(!nominalClass.IsSingletonClass);
            Debug.Assert(!nominalClass.IsRubyClass);

            // Do we need a singleton check?
            if (nominalClass.ClrSingletonMethods == null ||
                CollectionUtils.TrueForAll(resolvedNames, (methodName) => !nominalClass.ClrSingletonMethods.ContainsKey(methodName))) {

                // no: there is no singleton subclass of target class that defines any method being called:
                AddTypeRestriction(target.GetType(), targetParameter);
                AddVersionTest(targetClass);

            } else if (targetClass.IsSingletonClass) {

                // yes: check whether the incoming object is a singleton and the singleton has the right version:
                AddTypeRestriction(target.GetType(), targetParameter);
                AddCondition(Methods.IsClrSingletonRuleValid.OpCall(
                    metaContext.Expression,
                    targetParameter,
                    AstUtils.Constant(targetClass.Version.Method)
                ));

            } else {

                // yes: check whether the incoming object is NOT a singleton and the class has the right version:
                AddTypeRestriction(target.GetType(), targetParameter);
                AddCondition(Methods.IsClrNonSingletonRuleValid.OpCall(
                    metaContext.Expression, 
                    targetParameter,
                    Ast.Constant(targetClass.Version),
                    AstUtils.Constant(targetClass.Version.Method)
                ));
            }
        }
예제 #2
0
        public void AddTargetTypeTest(object target, RubyClass/*!*/ targetClass, Expression/*!*/ targetParameter, DynamicMetaObject/*!*/ metaContext) {

            // no changes to the module's class hierarchy while building the test:
            targetClass.Context.RequiresClassHierarchyLock();

            // initialization changes the version number, so ensure that the module is initialized:
            targetClass.InitializeMethodsNoLock();

            var context = (RubyContext)metaContext.Value;

            // singleton nil:
            if (target == null) {
                AddRestriction(Ast.Equal(targetParameter, AstUtils.Constant(null)));
                AddFullVersionTest(context.NilClass, metaContext);
                return;
            }

            // singletons true, false:
            if (target is bool) {
                AddRestriction(Ast.AndAlso(
                    Ast.TypeIs(targetParameter, typeof(bool)),
                    Ast.Equal(Ast.Convert(targetParameter, typeof(bool)), AstUtils.Constant(target))
                ));

                if ((bool)target) {
                    AddFullVersionTest(context.TrueClass, metaContext);
                } else {
                    AddFullVersionTest(context.FalseClass, metaContext);
                }
                return;

            }

            // user defined instance singletons, modules, classes:
            if (targetClass.IsSingletonClass) {
                AddRestriction(
                    Ast.Equal(
                        Ast.Convert(targetParameter, typeof(object)),
                        Ast.Convert(AstUtils.Constant(target), typeof(object))
                    )
                );

                // we need to check for a runtime (e.g. "foo" .NET string instance could be shared accross runtimes):
                AddFullVersionTest(targetClass, metaContext);
                return;
            }

            Type type = target.GetType();
            AddTypeRestriction(type, targetParameter);
            
            if (typeof(IRubyObject).IsAssignableFrom(type)) {
                // Ruby objects (get the method directly to prevent interface dispatch):
                MethodInfo classGetter = type.GetMethod("get_" + RubyObject.ClassPropertyName, BindingFlags.Public | BindingFlags.Instance);
                if (classGetter != null && classGetter.ReturnType == typeof(RubyClass)) {
                    AddCondition(
                        // (#{type})target.Class.Version.Value == #{immediateClass.Version}
                        Ast.Equal(
                            Ast.Field(
                                Ast.Field(
                                    Ast.Call(Ast.Convert(targetParameter, type), classGetter), 
                                    Fields.RubyClass_Version
                                ), 
                                Fields.StrongBox_Of_Int_Value
                            ),
                            AstUtils.Constant(targetClass.Version.Value)
                        )
                    );
                    return;
                }

                // TODO: explicit iface-implementation
                throw new NotSupportedException("Type implementing IRubyObject should have RubyClass getter");
            } else {
                // CLR objects:
                AddFullVersionTest(targetClass, metaContext);
            }
        }