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