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