[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";
+
+ }
+
+}