[Mono-bugs] [Bug 39383][Wis] New - Activator throws exception when null paramaters are passed to constructor

bugzilla-daemon@rocky.ximian.com bugzilla-daemon@rocky.ximian.com
Sun, 9 Mar 2003 13:35:14 -0500 (EST)


Please do not reply to this email- if you want to comment on the bug, go to the
URL shown below and enter your comments there.

Changed by tcabanski@oai.cc.

http://bugzilla.ximian.com/show_bug.cgi?id=39383

--- shadow/39383	Sun Mar  9 13:35:14 2003
+++ shadow/39383.tmp.26259	Sun Mar  9 13:35:14 2003
@@ -0,0 +1,468 @@
+Bug#: 39383
+Product: Mono/Class Libraries
+Version: unspecified
+OS: Mandrake 9.0
+OS Details: This issue should be consistent on all platforms
+Status: NEW   
+Resolution: 
+Severity: 
+Priority: Wishlist
+Component: CORLIB
+AssignedTo: mono-bugs@ximian.com                            
+ReportedBy: tcabanski@oai.cc               
+QAContact: mono-bugs@ximian.com
+TargetMilestone: ---
+URL: 
+Cc: 
+Summary: Activator throws exception when null paramaters are passed to constructor
+
+Activator cannot deal with null parameters because it uses GetConstructor 
+to fetch the constructor, which required the types of all parameters.  I 
+suspect Microsoft is using a method on InternalType so they do not have 
+this limitation.  The solution is to go lookup a matching constructor if 
+one or more parameters are null.  A constructor should be considered a 
+match if:
+
+1) It has the correct number of formal parameters
+2) The type of all non-null parameters matches the type of the 
+corresponding formal parameter.
+3) All null parameters correspond to non-value-type formal parameters.  
+That is, the corresponding formal parameters are objects and so the 
+caller can pass null.
+
+Here are the fixed methods in Activator.cs:
+
+public static object CreateInstance (Type type, object [] args, object [] 
+activationAttributes)
+{
+  if (type == null)
+    throw new ArgumentNullException ("type");
+
+  if (type.IsAbstract)
+    throw new MemberAccessException ("Cannot create an abstract class");
+
+  Type[] atypes;
+  ConstructorInfo ctor = FindConstructor(args, out atypes, type, 0, null);
+  if (ctor == null) 
+  {
+    if (type.IsValueType && atypes.Length == 0)
+      return CreateInstanceInternal (type);
+
+    throw new MissingMethodException ("Constructor not found");
+  }
+
+  if (activationAttributes != null && activationAttributes.Length > 0) 
+  {
+    object newOb = ActivationServices.CreateProxyFromAttributes (type, 
+activationAttributes);
+    if (newOb != null)
+      return ctor.Invoke (newOb, args);
+  }
+
+  return ctor.Invoke (args);
+}
+
+static private ConstructorInfo FindConstructor(object[] args, 
+  out Type[] atypes, 
+  Type type, 
+  BindingFlags bindingAttr,
+  Binder binder)
+{
+  int length = 0;
+  if (args != null)
+    length = args.Length;
+
+  atypes = new Type [length];
+  bool isNullArgs = false;
+  for (int i = 0; i < length; ++i) 
+  {
+    if (args [i] != null)
+    {
+      atypes [i] = args [i].GetType ();
+    } 
+    else 
+    {
+      isNullArgs = true;
+    }
+  }
+
+  ConstructorInfo ctor = null;
+
+  if (!isNullArgs)
+  {
+    if (bindingAttr > 0)
+    {
+      ctor = type.GetConstructor(bindingAttr, binder, atypes, null);
+    } 
+    else 
+    {
+      ctor = type.GetConstructor (atypes);
+    }
+  } 
+  else 
+  {
+    //if there are null args, find the first constructor that
+    //matches in number of formal parameters, has matching formal 
+    //parameter types for all non-null parameters
+    //and has non-value formal parameter types for all null parameters
+    ConstructorInfo [] ctors = null;
+    if (bindingAttr > 0)
+    {
+      ctors = type.GetConstructors(bindingAttr);
+    } 
+    else 
+    {
+      ctors = type.GetConstructors();
+    }
+
+    foreach (ConstructorInfo ctorItem in ctors)
+    {
+      ParameterInfo [] parms = ctorItem.GetParameters();
+      if (parms.Length == args.Length)
+      {
+        bool isMatch = true;
+        for (int i = 0; i < parms.Length; i++)
+        {
+          if (!((args [i] != null && atypes [i] == parms 
+[i].ParameterType) ||
+            (args [i] == null && !parms [i].ParameterType.IsValueType)))
+          {
+            isMatch = false;
+            break;
+          }
+        }
+        if (isMatch)
+        {
+          ctor = ctorItem;
+          break;
+        }
+      }
+    }
+  }
+
+  return ctor;
+}
+
+public static object CreateInstance (Type type,
+  BindingFlags bindingAttr,
+  Binder binder,
+  object [] args,
+  CultureInfo culture)
+{
+  return CreateInstance (type, bindingAttr, binder, args, culture, new 
+object [0]);
+}
+
+public static object CreateInstance (Type type,
+  BindingFlags bindingAttr,
+  Binder binder,
+  object [] args,
+  CultureInfo culture,
+  object [] activationAttributes)
+{
+  if (type == null)
+    throw new ArgumentNullException ("type");
+
+  if (type.IsAbstract)
+    throw new MemberAccessException ("Cannot create an abstract class");
+
+  int length = 0;
+  if (args != null)
+    length = args.Length;
+
+  Type[] atypes;
+  ConstructorInfo ctor = FindConstructor(args, out atypes, type, 
+bindingAttr, binder);
+  if (ctor == null) 
+  {
+    // Not sure about this
+    if (type.IsValueType && atypes.Length == 0) 
+    {
+      return CreateInstanceInternal (type);
+    }
+
+    throw new MissingMethodException ("Constructor not found");
+  }
+
+  if (activationAttributes != null && activationAttributes.Length > 0) 
+  {
+    object newOb = ActivationServices.CreateProxyFromAttributes (type, 
+activationAttributes);
+    if (newOb != null)
+      return ctor.Invoke (newOb, bindingAttr, binder, args, culture);
+  }
+
+  return ctor.Invoke (bindingAttr, binder, args, culture);
+}
+
+Here is the modified ActivatorTest.cs that includes tests to check on 
+simple construction and the passing of null paramaters.  Note that I 
+could not figure out a quick way to build the core test suite so I'm not 
+sure these tests are 100% correct:
+
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.Remoting;
+using System.Runtime.Remoting.Channels;
+using System.Runtime.Remoting.Channels.Tcp;
+using NUnit.Framework;
+using System.Reflection;
+
+// The class in this namespace is used by the
+// main test class
+namespace MonoTests.System.ActivatorTestInternal
+  {
+  // We need a COM class to test the Activator class
+  [ComVisible(true)]
+    
+  public class COMTest: MarshalByRefObject
+    {
+    public COMTest()
+      {
+      id = 0;
+      }
+    // This property is visible
+    [ComVisible(true)]
+      public int Id
+      {
+      get { return id; }
+      set { id = value; }
+      }
+    
+    public COMTest(int id)
+      {
+      this.id = id;
+      }
+    
+    private int id;
+    public bool constructorFlag = false;
+    }
+  } // MonoTests.System.ActivatorTestInternal namespace
+
+class TestActivation
+{
+  private string _s, _s1;
+  private int _i = -1;
+  private int _a, _b;
+
+  public TestActivation(string s, int i, int a)
+  {
+    _s = s;
+    _i = i;
+    _a = a;
+  }
+
+  public TestActivation(string s, string s1, int a)
+  {
+    _a = a;
+    _s = s;
+    if (s1 = null)
+    {
+      _s1 = "passed null";
+    } 
+    else 
+    {
+      _s1 = s1;
+    }
+  }
+
+  public TestActivation(int a, int b)
+  {
+    _a = a;
+    _b = b;
+  }
+
+  public string s
+  {
+    get
+    {
+      return _s;
+    }
+  }
+
+  public string s1
+  {
+    get
+    {
+      return _s1;
+    }
+  }
+
+  public int i
+  {
+    get
+    {
+      return _i;
+    }
+  }
+
+  public int a
+  {
+    get
+    {
+      return _a;
+    }
+  }
+}
+
+namespace MonoTests.System
+{
+  using MonoTests.System.ActivatorTestInternal;
+
+  [TestFixture]
+  public class ActivatorTest
+  {
+    public ActivatorTest()
+    {}
+    
+    [Test]
+    [Ignore("Activator.CreateComInstanceForm is not yet implemented")]
+      // This test is ignored for the moment because 
+      // CreateComInstanceFrom() is not implemented yet
+      // by the mono Activator class
+    public void CreateComInstanceFrom()
+    {
+      ObjectHandle objHandle = Activator.CreateComInstanceFrom
+(strAssembly ,
+        "COMTest");
+      COMTest objCOMTest = (COMTest) objHandle.Unwrap();
+      objCOMTest.Id = 10;
+      Assertion.AssertEquals("#A01",10,objCOMTest.Id);
+    }
+
+    [Test]
+      // This method tests CreateInstance()
+    public void CreateInstance()
+    {
+      COMTest objCOMTest;
+      // object CreateInstance(Type type)
+      objCOMTest = (COMTest) Activator.CreateInstance(typeof(COMTest));
+      Assertion.AssertEquals("#A02",
+        "MonoTests.System.ActivatorTestInternal.COMTest",
+        (objCOMTest.GetType()).ToString());
+      // ObjectHandle CreateInstance(string, string) 
+      ObjectHandle objHandle;
+      objHandle = Activator.CreateInstance(null ,
+        "MonoTests.System.ActivatorTestInternal.COMTest");
+      objCOMTest = (COMTest) objHandle.Unwrap();
+      objCOMTest.Id = 2;
+      Assertion.AssertEquals("#A03", 2, objCOMTest.Id);
+      // object CreateInstance(Type, bool)
+      objCOMTest = (COMTest) Activator.CreateInstance((typeof(COMTest)), 
+false);
+      Assertion.AssertEquals("#A04",
+        "MonoTests.System.ActivatorTestInternal.COMTest",
+        (objCOMTest.GetType()).ToString());
+      //       // object CreateInstance(Type, object[])
+      object[] objArray = new object[1];
+      objArray[0] = 7;
+      objCOMTest = (COMTest) Activator.CreateInstance((typeof(COMTest)), 
+objArray);
+      Assertion.AssertEquals("#A05", 7, objCOMTest.Id);
+      // Todo: Implemente the test methods for
+      // all the overriden functions using activationAttribute
+    }
+
+    // This method tests GetObject from the Activator class
+    [Test]
+    public void GetObject()
+    {
+      // object GetObject(Type, string)
+      
+      // This will provide a COMTest object on  
+tcp://localhost:1234/COMTestUri
+      COMTest objCOMTest = new COMTest(8);
+      TcpChannel chnServer = new TcpChannel(1234);
+      ChannelServices.RegisterChannel(chnServer);
+      RemotingServices.SetObjectUriForMarshal(objCOMTest, "COMTestUri");
+      RemotingServices.Marshal(objCOMTest);
+      
+      // This will get the remoting object
+      object objRem = Activator.GetObject(typeof(COMTest),
+        "tcp://localhost:1234/COMTestUri");
+      Assertion.Assert("#A07",objRem != null);
+      COMTest remCOMTest = (COMTest) objRem;
+      Assertion.AssertEquals("#A08", 8, remCOMTest.Id);
+
+      ChannelServices.UnregisterChannel(chnServer);
+      // Todo: Implemente the test methods for
+      // all the overriden function using activationAttribute
+    }
+
+    // This method tests the CreateInstanceFrom methods
+    // of the Activator class
+    [Test]
+    public void CreateInstanceFrom()
+    {
+      ObjectHandle objHandle;
+      objHandle = Activator.CreateInstanceFrom(strAssembly ,
+        "MonoTests.System.ActivatorTestInternal.COMTest");
+      Assertion.Assert("#A09", objHandle != null);
+      objHandle.Unwrap();
+      // Todo: Implement the test methods for
+      // all the overriden function using activationAttribute
+    }
+
+    [Test]
+    public void TestBasicConstruction()
+    {
+      Type t = Assembly.GetExecutingAssembly().GetType
+("MonoTests.System.ActivatorTestInternal.Test");
+      object [] o = new object[3];
+      o[0] = "one";
+      o[1] = "two";
+      o[2] = 6;
+      Test test = (Test)Activator.CreateInstance(t, o);
+      Assertion.Assert("Construction through Activator passed both non-
+null params ok", 
+        test.s == "one" && test.s1 == "two" && test.i == -1 && test.a == 
+6);
+
+      o[0] = "one";
+      o[1] = 1;
+      o[2] = 6;
+      Test test = (Test)Activator.CreateInstance(t, o);
+      Assertion.Assert("Construction through Activator passed both non-
+null params ok (case 2)", 
+        test.s == "one" && test.s1 == null && test.i == 1 && test.a == 6);
+    }
+
+    [Test]
+    public void TestBasicConstructionWithNullParam()
+    {
+      Type t = Assembly.GetExecutingAssembly().GetType
+("MonoTests.System.ActivatorTestInternal.Test");
+      object [] o = new object[3];
+      o[0] = "one";
+      o[1] = null;
+      o[2] = 6;
+      Test test = (Test)Activator.CreateInstance(t, o);
+      Assertion.Assert("Construction through Activator passed both null 
+params ok", 
+        test.s == "one" && test.s1 == "passed null" && test.i == -1 && 
+test.a == 6);
+    }
+
+    [Test]
+    [ExpectedException(typeof(MissingMethodException))]
+    public void TestBasicConstructionCannotFindSignature()
+    {
+      Type t = Assembly.GetExecutingAssembly().GetType
+("MonoTests.System.ActivatorTestInternal.Test");
+      object [] o = new object[3];
+      o[0] = null;
+      o[1] = null;
+      o[2] = null;
+      Test test = (Test)Activator.CreateInstance(t, o);
+    }
+    
+    // The name of the assembly file is incorrect.
+    // I used it to test these classes but you should
+    // replace it with the name of the mono tests assembly file
+    // The name of the assembly is used to get an object through
+    // Activator.CreateInstance(), Activator.CreateComInstanceFrom()...
+    private string strAssembly = "corlib_test.dll";
+    
+  }
+  
+}