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) )); } }
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); } }