Automtatic type conversion when calling CLR methods

Nov 18, 2009 at 2:21 PM

The project is getting along quite nicely! 

One are that would be nice if the engine automatically would convert arguments when calling or instantiating CLR methods and classes, because this syntax is really a hassle:

var adate = new System.DateTime(ToInt32(2010), ToInt32(1), ToInt32(1));

It would be much nicer if this would work:

var n = new System.DateTime(2010, 1, 1);
01.01.2010 00:00:00

And it also would be much more in line with how one think a javascript should work.

var n2 = n.AddSeconds(15000);
11.10.2010 04:10:00
var n3 = n2.AddTicks(150000000);     //AddTicks expect a long / Int64
11.10.2010 04:10:15

I have made some changes to the source code of 0.8.5 to make it work automatic:

CachedMethodInvoker.cs:

        static Dictionary<Type, Dictionary<string, MethodInformation>> _Cache = new Dictionary<Type, Dictionary<string, MethodInformation>>();

        class MethodInformation 
        {
            public MethodInfo MethodInfo;
            public bool ConversionRequired;
            public Type [] ConvertToTypes;
        }
// some skipped lines ....
       public bool Invoke(object subject, string method, object[] parameters, ref object result)
        {
            Type[] types = new Type[parameters.Length]; 
            object [] convertedParameters = new object[parameters.Length];
            
            for (int j = 0; j < parameters.Length; j++)
            {
                types[j] = parameters[j].GetType();
                convertedParameters[j] = parameters[j];
            }


            lock (_Cache)
            {
                MethodInformation methodInformation = null;
                string key = GetCacheKey(method, parameters);

                // Static evaluation
                bool isStaticCall = subject is Type;
                Type type = isStaticCall ? (Type)subject : subject.GetType();

                if (!_Cache.ContainsKey(type))
                {
                    _Cache.Add(type, new Dictionary<string, MethodInformation>());
                }

                bool parametersIsConverted = false;
                if (!_Cache[type].ContainsKey(key)) {

                    MethodInfo methodInfo = type.GetMethod(method, types);
                    if (methodInfo == null && types.Length > 0) {
                        // Search for similar method (method with same argument count and with convertable arguments)
                        foreach (MethodInfo mi in type.GetMethods()) {
                            if (mi.Name == method) {
                                ParameterInfo[] pis = mi.GetParameters();
                                if (pis.Length == types.Length) {
                                    // Same number of parameters
                                    // Try to convert parameters to the argument types
                                    Type [] convertToTypes = new Type[types.Length];
                                    bool success = true;
                                    for (int i = 0; i < types.Length; i++) {
                                        try {
                                            convertedParameters[i] = Convert.ChangeType(parameters[i], pis[i].ParameterType);
                                            convertToTypes[i] = pis[i].ParameterType;
                                        }
                                        catch {
                                            success = false;
                                            break;
                                        }
                                    }
                                    if (success) {
                                        methodInformation = new MethodInformation() { MethodInfo = mi, 
                                            ConversionRequired = true, 
                                            ConvertToTypes = convertToTypes 
                                        };
                                        parametersIsConverted = true;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    else {
                        methodInformation = new MethodInformation() { MethodInfo = methodInfo, ConversionRequired = false };
                    }
                    _Cache[type].Add(key, methodInformation);
                }
                else {
                    methodInformation = _Cache[type][key];
                }

                if (methodInformation != null)
                {
                    // Since using a cache, the values may need to be converted
                    if (methodInformation.ConversionRequired && !parametersIsConverted) {
                        for (int i = 0; i < types.Length; i++) {
                            convertedParameters[i] = Convert.ChangeType(parameters[i], methodInformation.ConvertToTypes[i]);
                        }
                    }

                    result = methodInformation.MethodInfo.Invoke(subject, convertedParameters);
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

ExecutionVisitor.cs(589):

            object[] values = new object[parameters.Length];
            Type[] types = new Type[parameters.Length];

            for (int i = 0; i < parameters.Length; i++)
            {
                values[i] = parameters[i].Value;
                types[i] = (values[i] ?? new Object()).GetType();
            }

            ConstructorInfo constructorInfo = type.GetConstructor(types);
            if (constructorInfo == null && types.Length > 0) {
                // Search for mathcing constructur using converted values
                foreach (ConstructorInfo ci in type.GetConstructors()) {
                    ParameterInfo[] pis = ci.GetParameters();
                    if (pis.Length == types.Length) {
                        // Same number of parameters
                        // Try to convert parameters to the argument types
                        bool success = true;
                        for (int i = 0; i < types.Length; i++) {
                            try {
                                values[i] = Convert.ChangeType(parameters[i].Value, pis[i].ParameterType);
                            }
                            catch {
                                success = false;
                                break;
                            }
                        }
                        if (success) {
                            constructorInfo = ci;
                            break;
                        }
                    }
                }
            }

            if (constructorInfo != null) {
                Result = new JsClr(Activator.CreateInstance(type, values));
            }

            if (Result == null || constructorInfo == null)
            {
                throw new JintException("Matching constructor not found for: " + fullName);
            }

I hope something like this could be integrated into the source code!

Kåre 

 

Coordinator
Nov 18, 2009 at 2:24 PM

I will definitely try to implement your suggestion. Thanks.