using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using Jint.Native;
using Jint.Native.Object;
using Jint.Runtime.Descriptors;
namespace Jint.Runtime.Interop
{
///
/// Any instance on this class represents a reference to a CLR namespace.
/// Accessing its properties will look for a class of the full name, or instantiate
/// a new as it assumes that the property is a deeper
/// level of the current namespace
///
public class NamespaceReference : ObjectInstance, ICallable
{
private readonly string _path;
public NamespaceReference(Engine engine, string path) : base(engine)
{
_path = path;
}
public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
{
if (throwOnError)
{
throw new JavaScriptException(Engine.TypeError, "Can't define a property of a NamespaceReference");
}
return false;
}
public override bool Delete(string propertyName, bool throwOnError)
{
if (throwOnError)
{
throw new JavaScriptException(Engine.TypeError, "Can't delete a property of a NamespaceReference");
}
return false;
}
public JsValue Call(JsValue thisObject, JsValue[] arguments)
{
// direct calls on a NamespaceReference constructor object is creating a generic type
var genericTypes = new Type[arguments.Length];
for (int i = 0; i < arguments.Length; i++)
{
var genericTypeReference = arguments.At(i);
if (genericTypeReference == Undefined.Instance || !genericTypeReference.IsObject() || genericTypeReference.AsObject().Class != "TypeReference")
{
throw new JavaScriptException(Engine.TypeError, "Invalid generic type parameter");
}
genericTypes[i] = arguments.At(i).As().Type;
}
var typeReference = GetPath(_path + "`" + arguments.Length.ToString(CultureInfo.InvariantCulture)).As();
if (typeReference == null)
{
return Undefined.Instance;
}
var genericType = typeReference.Type.MakeGenericType(genericTypes);
return TypeReference.CreateTypeReference(Engine, genericType);
}
public override JsValue Get(string propertyName)
{
var newPath = _path + "." + propertyName;
return GetPath(newPath);
}
public JsValue GetPath(string path)
{
Type type;
if (Engine.TypeCache.TryGetValue(path, out type))
{
if (type == null)
{
return new NamespaceReference(Engine, path);
}
return TypeReference.CreateTypeReference(Engine, type);
}
// search for type in mscorlib
type = Type.GetType(path);
if (type != null)
{
Engine.TypeCache.Add(path, type);
return TypeReference.CreateTypeReference(Engine, type);
}
// search in loaded assemblies
#if NETSTANDARD1_3
var lookupAssemblies = new[] { typeof(NamespaceReference).GetTypeInfo().Assembly };
#else
var lookupAssemblies = new[] { Assembly.GetCallingAssembly(), Assembly.GetExecutingAssembly() };
#endif
foreach (var assembly in lookupAssemblies)
{
type = assembly.GetType(path);
if (type != null)
{
Engine.TypeCache.Add(path, type);
return TypeReference.CreateTypeReference(Engine, type);
}
}
// search in lookup assemblies
foreach (var assembly in Engine.Options._LookupAssemblies)
{
type = assembly.GetType(path);
if (type != null)
{
Engine.TypeCache.Add(path, type);
return TypeReference.CreateTypeReference(Engine, type);
}
var lastPeriodPos = path.LastIndexOf(".", StringComparison.Ordinal);
var trimPath = path.Substring(0, lastPeriodPos);
type = GetType(assembly, trimPath);
if (type != null)
foreach (Type nType in GetAllNestedTypes(type))
{
if (nType.FullName.Replace("+", ".").Equals(path.Replace("+", ".")))
{
Engine.TypeCache.Add(path.Replace("+", "."), nType);
return TypeReference.CreateTypeReference(Engine, nType);
}
}
}
// the new path doesn't represent a known class, thus return a new namespace instance
Engine.TypeCache.Add(path, null);
return new NamespaceReference(Engine, path);
}
/// Gets a type.
///Nested type separators are converted to '.' instead of '+'
/// The assembly.
/// Name of the type.
///
/// The type.
private static Type GetType(Assembly assembly, string typeName)
{
Type[] types = assembly.GetTypes();
foreach (Type t in types)
{
if (t.FullName.Replace("+", ".") == typeName.Replace("+", "."))
{
return t;
}
}
return null;
}
private static IEnumerable GetAllNestedTypes(Type type)
{
var types = new List();
AddNestedTypesRecursively(types, type);
return types.ToArray();
}
private static void AddNestedTypesRecursively(List types, Type type)
{
Type[] nestedTypes = type.GetNestedTypes(BindingFlags.Public);
foreach (Type nestedType in nestedTypes)
{
types.Add(nestedType);
AddNestedTypesRecursively(types, nestedType);
}
}
public override PropertyDescriptor GetOwnProperty(string propertyName)
{
return PropertyDescriptor.Undefined;
}
public override string ToString()
{
return "[Namespace: " + _path + "]";
}
}
}