Beispiel #1
0
        public static JsTypeFunction Define(JsFunction assembly, JsTypeFunction enclosingType, string name, bool isGenericType, JsArray typeParameters, JsObject prototype, JsFunction typeInitializer)
        {
            JsTypeFunction typeFunction      = null;
            var            isTypeInitialized = false;

            // Create constructor function, which is a superconstructor that takes in the actual
            // constructor as the first argument, and the rest of the arguments are passed directly
            // to that constructor.  These subconstructors are not Javascript constructors -- they
            // are not called via new, they exist for initialization only.
            typeFunction = Jsni.function((constructor, args) =>
            {
                if (constructor != null || !(Jsni.instanceof(Jsni.@this(), typeFunction)))
                {
                    if (!isGenericType || typeFunction.UnconstructedType != null)
                    {
                        typeFunction.member(SpecialNames.StaticInitializer).invoke();
                    }
                }
                if (constructor != null)
                {
                    constructor.apply(Jsni.@this(), args.As <JsArray>());
                }
                if (!Jsni.instanceof(Jsni.@this(), typeFunction))
                {
                    return(typeFunction);
                }
                else
                {
                    return(Jsni.@this());
                }
            }).As <JsTypeFunction>();
            typeFunction.GetAssembly = assembly;
            assembly.member(SpecialNames.AssemblyTypesArray).member("push").invoke(typeFunction);
            typeFunction.memberset("toString", Jsni.function(() => name.As <JsObject>()));
            typeFunction.EnclosingType          = enclosingType;
            typeFunction.TypeName               = name;
            typeFunction.prototype              = Jsni.@new(prototype);
            typeFunction.IsPrototypeInitialized = false;
            typeFunction.TypeInitializer        = Jsni.procedure((_t, p) =>
            {
                var t = _t.As <JsTypeFunction>();
                if (isGenericType)
                {
                    var unconstructedType = t.UnconstructedType ?? t;
                    t.GenericTypeFunction = Jsni.function(() =>
                    {
                        return(Jsni.reference(SpecialNames.MakeGenericTypeConstructor).As <JsFunction>().call(unconstructedType, unconstructedType, Jsni.arguments()).As <JsFunction>().invoke());
                    });
                }
                t.GetTypeFromType = Jsni.function(() =>
                {
                    return(Type._GetTypeFromTypeFunc(Jsni.@this().As <JsTypeFunction>()).As <JsObject>());
                });
                p.memberset(SpecialNames.TypeName, t.member(SpecialNames.TypeName));
                p.___type  = t;
                t.BaseType = prototype.As <JsTypeFunction>();

                typeInitializer.apply(Jsni.@this(), Jsni.arguments().As <JsArray>());
            });
            typeFunction.CallTypeInitializer = Jsni.procedure(() =>
            {
                typeFunction.TypeInitializer.apply(enclosingType, Jsni.array(typeFunction, typeFunction.prototype).concat(typeParameters));
            });
            return(typeFunction);
        }
 public static object FromJsonObject(this JsObject o, Type type)
 {
     if (type.IsArray)
     {
         var arrayValue  = o.As <JsArray>();
         var elementType = type.GetElementType();
         var array       = Array.CreateInstance(elementType, arrayValue.length);
         for (var i = 0; i < arrayValue.length; i++)
         {
             array.SetValue(FromJsonObject(arrayValue[i], elementType), i);
         }
         return(array.As <JsObject>());
     }
     else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List <>))
     {
         var arrayValue  = o.As <JsArray>();
         var elementType = type.GetGenericArguments()[0];
         var list        = (IList)Activator.CreateInstance(type);
         for (var i = 0; i < arrayValue.length; i++)
         {
             list.Add(FromJsonObject(arrayValue[i], elementType));
         }
         return(list.As <JsObject>());
     }
     else if (type.IsPrimitive)
     {
         return(Convert.ChangeType(o, type));
     }
     else if (type == typeof(DateTime) || (type.IsNullableValueType() && type.GetGenericArguments()[0] == typeof(DateTime)))
     {
         if (o == null)
         {
             return(null);
         }
         return(DateTime.ParseExact(o.As <string>(), Iso8601).As <JsObject>());
     }
     else if (type == typeof(string))
     {
         return(o.As <string>());
     }
     else if (type.IsEnum)
     {
         return(Enum.Parse(type, o.As <string>()));
     }
     else
     {
         var result     = Activator.CreateInstance(type);
         var properties = type.GetProperties().ToDictionary(x => x.Name.ToUpper());
         foreach (var propertyName in o)
         {
             var          value = o[propertyName];
             PropertyInfo property;
             if (properties.TryGetValue(propertyName.ToUpper(), out property))
             {
                 var newValue = FromJsonObject(value, property.PropertyType);
                 property.SetValue(result, newValue, null);
             }
         }
         return(result);
     }
 }
Beispiel #3
0
        internal static JsTypeFunction MakeGenericTypeFactory(JsTypeFunction unconstructedType, JsArray typeArgs)
        {
            var      cache  = Jsni.member(unconstructedType, SpecialNames.TypeCache);
            JsObject result = null;

            if (typeArgs.length > 0)
            {
                if (cache == null)
                {
                    cache = new JsObject();
                    unconstructedType.memberset(SpecialNames.TypeCache, cache);
                }

                JsObject currentCache = cache;
                for (var i = 0; i < typeArgs.length - 1; i++)
                {
                    var nextCache = currentCache[typeArgs[i].member(SpecialNames.TypeName)];
                    if (nextCache == null)
                    {
                        nextCache = new JsObject();
                        currentCache[typeArgs[i].member(SpecialNames.TypeName)] = nextCache;
                    }
                    currentCache = nextCache;
                }
                cache  = currentCache;
                result = cache[typeArgs[typeArgs.length - 1].member(SpecialNames.TypeName)];
            }
            else
            {
                result = cache;
            }

//            JsObject keyString;

            // First two if statements are optimizations for performance reasons to avoid heap allocation when possible.

/*
 *          if (typeArgs.length == 0)
 *              keyString = "";
 *          else if (typeArgs.length == 1)
 *              keyString = typeArgs[0].member(SpecialNames.TypeName);
 *          else
 *          {
 *              var keyArray = Jsni.call<JsArray>(x => x.slice(0), typeArgs, 0.As<JsNumber>()).As<JsArray>();
 *              var keyParts = new JsArray();
 *              for (var i = 0; i < keyArray.length; i++)
 *              {
 *                  keyParts[i] = keyArray[i].member(SpecialNames.TypeName);
 *              }
 *              keyString = keyParts.join(", ");
 *          }
 *
 *          var result = cache[keyString];
 */
            if (result == null)
            {
                var keyArray = Jsni.call <JsArray>(x => x.slice(0), typeArgs, 0.As < JsNumber > ()).As <JsArray>();
                var keyParts = new JsArray();
                for (var i = 0; i < keyArray.length; i++)
                {
                    keyParts[i] = keyArray[i].member(SpecialNames.TypeName);
                }
                var keyString = keyParts.join(", ");

                var lastIndexOfDollar = unconstructedType.TypeName.LastIndexOf('`');
                if (lastIndexOfDollar == -1)
                {
                    lastIndexOfDollar = unconstructedType.TypeName.Length;
                }
                var newTypeName = unconstructedType.TypeName.Substring(0, lastIndexOfDollar) + "<" + keyString + ">";
                var prototype   = unconstructedType.BaseType;
                if (prototype.member("$"))
                {
                    var baseArgs = unconstructedType.BaseTypeArgs.slice(0);
                    for (var i = 0; i < baseArgs.length; i++)
                    {
                        var baseArg = (JsTypeFunction)baseArgs[i];
                        if (baseArg.IsTypeParameter)
                        {
                            for (var j = 0; j < unconstructedType.TypeArgs.length; j++)
                            {
                                var typeArg = unconstructedType.TypeArgs[j];
                                if (typeArg == baseArg)
                                {
                                    baseArgs[i] = typeArgs[j];
                                }
                            }
                        }
                    }
                    prototype = prototype.member("$").apply(null, baseArgs).As <JsTypeFunction>();
                }
                var typeInitializer = Jsni.procedure((_t, p) =>
                {
                    var t     = _t.As <JsTypeFunction>();
                    p.___type = t;
                    t.As <JsTypeFunction>().BaseType        = unconstructedType;
                    t.As <JsTypeFunction>().GetTypeFromType = Jsni.function(() => Type._GetTypeFromTypeFunc(Jsni.@this().As <JsTypeFunction>()).As <JsObject>());
                    t.As <JsTypeFunction>().TypeName        = newTypeName;
                    t.As <JsTypeFunction>().CreateTypeField = Jsni.function(() =>
                    {
                        var unconstructedTypeType = Type._GetTypeFromTypeFunc(unconstructedType);
                        var type      = new Type(newTypeName, new Attribute[0]);
                        t.Type        = type;
                        type.thisType = t;

                        var typeParameters = unconstructedTypeType.typeArguments;
                        var typeArguments  = InitializeArray(typeArgs, Jsni.type <JsTypeFunction>()).As <JsTypeFunction[]>();

                        Func <Type, JsTypeFunction> reifyGenerics = null;
                        reifyGenerics = theType =>
                        {
                            if (theType == null)
                            {
                                return(null);
                            }
                            if (theType.IsArray)
                            {
                                var elementType = theType.GetElementType();
                                var arrayType   = MakeArrayType(reifyGenerics(elementType));
                                return(arrayType);
                            }
                            if (theType.IsGenericParameter)
                            {
                                for (int i = 0; i < typeArguments.Length; i++)
                                {
                                    var typeParameter = typeParameters[i];
                                    var typeArgument  = typeArguments[i];
                                    if (typeParameter.TypeName == theType.FullName)
                                    {
                                        return(typeArgument);
                                    }
                                }
                            }
                            else if (theType.IsGenericTypeDefinition)
                            {
                                return(MakeGenericTypeFactory(theType.thisType, theType.GenericTypeArguments.Select(x => reifyGenerics(x)).ToArray().As <JsArray>()));
                            }
                            else if (theType.IsGenericType)
                            {
                                JsTypeFunction[] newTypeArguments = null;
                                for (var i = 0; i < theType.typeArguments.Length; i++)
                                {
                                    var theTypeArgument = theType.typeArguments[i];
                                    for (var j = 0; j < typeParameters.Length; j++)
                                    {
                                        var typeParameter = typeParameters[j];
                                        var typeArgument  = typeArguments[j];
                                        if (theTypeArgument == typeParameter)
                                        {
                                            if (newTypeArguments == null)
                                            {
                                                newTypeArguments = new JsTypeFunction[theType.typeArguments.Length];
                                                for (var k = 0; k < theType.typeArguments.Length; k++)
                                                {
                                                    newTypeArguments[k] = theType.typeArguments[k];
                                                }
                                            }
                                            newTypeArguments[i] = typeArgument;
                                        }
                                    }
                                }
                                if (newTypeArguments != null)
                                {
                                    return(MakeGenericTypeFactory(theType.unconstructedType, newTypeArguments.As <JsArray>()));
                                }
                                else
                                {
                                    return(theType.thisType);
                                }
                            }
                            return(theType.thisType);
                        };

                        var newInterfaces = unconstructedTypeType.interfaces.ToArray();
                        for (var i = 0; i < newInterfaces.Length; i++)
                        {
                            var intf         = newInterfaces[i];
                            intf             = reifyGenerics(Type._GetTypeFromTypeFunc(intf));
                            newInterfaces[i] = intf;
                        }

                        var newConstructors = unconstructedTypeType.constructors.ToArray();
                        for (var i = 0; i < newConstructors.Length; i++)
                        {
                            var constructor = newConstructors[i];
                            var parameters  = constructor.GetParameters();
                            for (var j = 0; j < parameters.Length; j++)
                            {
                                var parameter     = parameters[j];
                                var parameterType = reifyGenerics(parameter.ParameterType);
                                parameter         = new ParameterInfo(parameter.Name, parameterType, j, parameter.Attributes, parameter.DefaultValue, parameter.attributes);
                                parameters[j]     = parameter;
                            }
                            newConstructors[i] = new ConstructorInfo(constructor.Name, t.prototype[constructor.Name].As <JsFunction>(), parameters, constructor.Attributes, constructor.attributes);
                        }

                        var newProperties = unconstructedTypeType.properties.ToArray();
                        for (var i = 0; i < newProperties.Length; i++)
                        {
                            var property         = newProperties[i];
                            var propertyTypeType = property.PropertyType;
                            var propertyType     = reifyGenerics(propertyTypeType);
                            newProperties[i]     = new PropertyInfo(property.Name, propertyType, property.GetGetMethod(), property.GetSetMethod(),
                                                                    property.GetIndexParameters(), property.attributes);
                        }

                        var newMethods = unconstructedTypeType.methods.ToArray();
                        for (var i = 0; i < newMethods.Length; i++)
                        {
                            var method         = newMethods[i];
                            var returnTypeType = method.ReturnType;
                            var returnType     = reifyGenerics(returnTypeType);
                            var parameters     = method.GetParameters();
                            for (var j = 0; j < parameters.Length; j++)
                            {
                                var parameter     = parameters[j];
                                var parameterType = reifyGenerics(parameter.ParameterType);
                                parameter         = new ParameterInfo(parameter.Name, parameterType, j, parameter.Attributes, parameter.DefaultValue, parameter.attributes);
                                parameters[j]     = parameter;
                            }
                            method        = new MethodInfo(method.Name, method.jsMethod, parameters, returnType, method.Attributes, method.attributes);
                            newMethods[i] = method;
                        }

                        type.Init(
                            newTypeName,
                            (int)((unconstructedTypeType.typeFlags | TypeFlags.GenericType) & ~TypeFlags.GenericTypeDefenition),
                            t,
                            prototype,
                            newInterfaces,
                            typeArguments,
                            unconstructedTypeType.fields,
                            newMethods,
                            newConstructors,
                            newProperties,
                            unconstructedTypeType.events,
                            null,
                            unconstructedType);
                        return(type.As <JsObject>());
                    });
                }, SpecialNames.TypeInitializerTypeFunction, SpecialNames.TypeInitializerPrototype);

                var generic = Define(unconstructedType.GetAssembly, unconstructedType.EnclosingType, newTypeName, true, Jsni.array(), prototype, typeInitializer);
                generic.memberset(SpecialNames.UnconstructedType, unconstructedType);

                // unconstructedType.$TypeInitializer.apply(this, [generic, generic.prototype].concat(Array.prototype.slice.call(arguments, 0)));
                unconstructedType.member(SpecialNames.TypeInitializer).apply(
                    unconstructedType,
                    Jsni.array(generic, generic.prototype).member("concat").invoke(keyArray).As <JsArray>()
                    );

                Jsni.call(generic.TypeInitializer, Jsni.@this(), generic, generic.prototype);
                result = generic;

                if (typeArgs.length == 0)
                {
                    unconstructedType.memberset(SpecialNames.TypeCache, result);
                }
                else
                {
                    cache[typeArgs[typeArgs.length - 1].member(SpecialNames.TypeName)] = result;
                }
            }

            return(result.As <JsTypeFunction>());
        }