Skip to content

Commit

Permalink
[TIMOB-24637] Windows: Accessing generic collections throws exception (
Browse files Browse the repository at this point in the history
…#165) (#170)

* [TIMOB-24637] Windows: Accessing generic collections throws exception
* [TIMOB-24646] Implement indexer property
* Implement cast to JavaScript value from native object
* Failsafe ConvertNumber
  • Loading branch information
infosia committed May 4, 2017
1 parent 6ce429f commit 3c6a3b4
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 20 deletions.
136 changes: 118 additions & 18 deletions windows/reflection/HyperloopInvocation/src/HyperloopInvocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,24 @@ public bool IsBoolean()
}
public bool IsNumber()
{
return (NativeType == typeof(System.Double) ||
NativeType == typeof(System.Single) ||
NativeType == typeof(System.Decimal) ||
NativeType == typeof(System.Int64) ||
NativeType == typeof(System.Int32) ||
NativeType == typeof(System.Int16) ||
NativeType == typeof(System.UInt64) ||
NativeType == typeof(System.UInt32) ||
NativeType == typeof(System.UInt16) ||
NativeType == typeof(System.SByte) ||
NativeType == typeof(System.Byte));
return CanConvertToNumber(NativeType);
}

public static bool CanConvertToNumber(Type type)
{
return (type == typeof(System.Double) ||
type == typeof(System.Single) ||
type == typeof(System.Decimal) ||
type == typeof(System.Int64) ||
type == typeof(System.Int32) ||
type == typeof(System.Int16) ||
type == typeof(System.UInt64) ||
type == typeof(System.UInt32) ||
type == typeof(System.UInt16) ||
type == typeof(System.SByte) ||
type == typeof(System.Byte));
}

public bool IsObject()
{
return NativeObject is object;
Expand All @@ -107,7 +113,14 @@ public bool IsNull()

public static object ConvertNumber(Type nativeType, double number)
{
return System.Convert.ChangeType(number, nativeType);
if (CanConvertToNumber(nativeType))
{
return System.Convert.ChangeType(number, nativeType);
}
else
{
return System.Convert.ChangeType(number, typeof(System.Double));
}
}

public object addEventListener(string name, object target, Type helper)
Expand Down Expand Up @@ -256,16 +269,28 @@ public static Method GetMethod(Type type, string name, [ReadOnlyArray()] Type[]
MethodInfo methodInfo = type.GetRuntimeMethod(name, parameters == null ? new Type[0] : parameters);
if (methodInfo == null)
{
return null;
Type[] interfaces = type.GetInterfaces();
foreach (Type i in interfaces)
{
Method m = GetMethod(i, name, parameters);
if (m != null)
{
return m;
}
}
}
else
{
Method method = new Method(name);
method.methodInfo = methodInfo;
return method;
}
Method method = new Method(name);
method.methodInfo = methodInfo;
return method;
}
catch
{
return null;
// Do nothing
}
return null;
}
private static bool TryGetCachedMethod(Type type, string name, int expectedCount, out IList<Method> cachedMethods)
{
Expand Down Expand Up @@ -340,6 +365,16 @@ public static IList<Method> GetMethods(Type type, string name, int expectedCount
}
}

var interfaces = type.GetInterfaces();
foreach (Type i in interfaces)
{
var iMethods = GetMethods(i, name, expectedCount);
foreach(Method m in iMethods)
{
methodList.Add(m);
}
}

UpdateCache(type, name, expectedCount, methodList);

return methodList;
Expand Down Expand Up @@ -369,6 +404,15 @@ public static bool HasMethod(Type type, string name)
}
}

var interfaces = type.GetInterfaces();
foreach (Type i in interfaces)
{
if (HasMethod(i, name))
{
return true;
}
}

// We can't find the method, then we marks it as "not available"
if (cached == null)
{
Expand All @@ -386,6 +430,8 @@ public static bool HasMethod(Type type, string name)
public sealed class Property
{
public string Name { get; set; }
public int Index { get; set; }
public bool IsIndexer { get; set; }
private PropertyInfo propertyInfo;
private FieldInfo fieldInfo;
public Property(string name)
Expand All @@ -399,6 +445,18 @@ public Instance GetValue(Instance instance)
{
obj = instance.NativeObject;
}

if (IsIndexer)
{
Type[] indexParams = { typeof(Int32) };
Instance[] indexArgs = { new Instance(indexParams[0], Index) };
Method m = Method.GetMethod(propertyInfo.DeclaringType, "get_" + Name, indexParams);
if (m != null)
{
return m.Invoke(instance, indexArgs);
}
}

if (propertyInfo != null)
{
object value = propertyInfo.GetValue(obj);
Expand All @@ -416,7 +474,20 @@ public void SetValue(Instance instance, Instance value)
{
if (propertyInfo != null)
{
propertyInfo.SetValue(instance.NativeObject, value.NativeObject);
// Array-style property access such as object[0]
if (IsIndexer)
{
Type[] indexParams = { typeof(Int32), value.NativeType };
Instance[] indexArgs = { new Instance(indexParams[0], Index), value };
Method m = Method.GetMethod(propertyInfo.DeclaringType, "set_" + Name, indexParams);
if (m != null)
{
m.Invoke(instance, indexArgs);
}
} else
{
propertyInfo.SetValue(instance.NativeObject, value.NativeObject);
}
}
}
public Type GetPropertyType()
Expand All @@ -434,6 +505,24 @@ public Type GetPropertyType()

public static Property GetProperty(Type type, string name)
{
// Array-style property access such as object[0]
int index = 0;
if (Int32.TryParse(name, out index))
{
PropertyInfo[] properties = type.GetProperties();
foreach (var prop in properties)
{
if (prop.GetIndexParameters().Length > 0)
{
Property property = new Property(prop.Name);
property.propertyInfo = prop;
property.Index = index;
property.IsIndexer = true;
return property;
}
}
}

PropertyInfo propertyInfo = type.GetRuntimeProperty(name);
if (propertyInfo != null)
{
Expand All @@ -451,6 +540,17 @@ public static Property GetProperty(Type type, string name)
return property;
}

// Interfaces
Type[] interfaces = type.GetInterfaces();
foreach(Type i in interfaces)
{
Property property = GetProperty(i, name);
if (property != null)
{
return property;
}
}

return null;
}
public static bool HasProperty(Type type, string name)
Expand Down
16 changes: 14 additions & 2 deletions windows/src/Hyperloop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,16 +317,28 @@ TITANIUM_FUNCTION(HyperloopInstance, cast)
if (arguments.size() == 0) {
return get_context().CreateNull();
}
const auto ctx = get_context();
const auto _0 = arguments.at(0);
if (_0.IsObject()) {
const auto obj = static_cast<JSObject>(_0);
const auto obj_ptr = obj.GetPrivate<HyperloopInstance>();
if (obj_ptr) {
obj_ptr->set_type(type__);
// We should create new instance with new type because original object should not be altered with cast.
const auto instance = ref new HyperloopInvocation::Instance(type__, obj_ptr->get_instance()->NativeObject);

//
// We have a way to convert native object into JavaScript types.
// i.e. require('System.Double').cast(obj); returns JavaScript Number
//
if (instance->IsBoolean() || instance->IsNumber() || instance->IsString()) {
return HyperloopModule::Convert(ctx, instance);
}

return HyperloopModule::CreateObject(ctx, instance);
}
return obj;
} else {
return HyperloopModule::CreateObject(get_context(), HyperloopModule::Convert(_0, type__));
return HyperloopModule::CreateObject(ctx, HyperloopModule::Convert(_0, type__));
}
return _0;
}
Expand Down

0 comments on commit 3c6a3b4

Please sign in to comment.