Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 64 additions & 101 deletions ClearScript/HostItem.InvokeMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private object InvokeMethod(string name, object[] args, object[] bindArgs)
private object InvokeMethod(string name, Type[] typeArgs, object[] args, object[] bindArgs)
{
var bindResult = BindMethod(name, typeArgs, args, bindArgs);
if ((bindResult is MethodBindFailure) && Target.GetFlags(this).HasFlag(HostTargetFlags.AllowExtensionMethods))
if (!bindResult.IsSuccess && Target.GetFlags(this).HasFlag(HostTargetFlags.AllowExtensionMethods))
{
var targetArg = Target.Target.ToEnumerable();
var extensionArgs = targetArg.Concat(args).ToArray();
Expand All @@ -65,7 +65,7 @@ private object InvokeMethod(string name, Type[] typeArgs, object[] args, object[
{
var extensionHostItem = (HostItem)Wrap(Engine, HostType.Wrap(type));
var extensionBindResult = extensionHostItem.BindMethod(name, typeArgs, extensionArgs, extensionBindArgs);
if (extensionBindResult is MethodBindSuccess)
if (extensionBindResult.IsSuccess)
{
var result = extensionBindResult.Invoke(extensionHostItem);
for (var index = 1; index < extensionArgs.Length; index++)
Expand Down Expand Up @@ -121,16 +121,16 @@ private MethodBindResult BindMethod(string name, Type[] typeArgs, object[] args,

if (forceReflection)
{
result = new MethodBindFailure(() => new MissingMethodException(MiscHelpers.FormatInvariant("The object has no method named '{0}' that matches the specified arguments", name)));
result = new MethodBindResult(() => new MissingMethodException(MiscHelpers.FormatInvariant("The object has no method named '{0}' that matches the specified arguments", name)));
}
else
{
result = BindMethodInternal(signature, AccessContext, bindFlags, Target, name, typeArgs, args, bindArgs);
if (!result.IsPreferredMethod(this, name))
{
if (result is MethodBindSuccess)
if (result.IsSuccess)
{
result = new MethodBindFailure(() => new MissingMethodException(MiscHelpers.FormatInvariant("The object has no method named '{0}' that matches the specified arguments", name)));
result = new MethodBindResult(() => new MissingMethodException(MiscHelpers.FormatInvariant("The object has no method named '{0}' that matches the specified arguments", name)));
}

foreach (var altName in GetAltMethodNames(name, bindFlags))
Expand All @@ -145,10 +145,10 @@ private MethodBindResult BindMethod(string name, Type[] typeArgs, object[] args,
}
}

if ((result is MethodBindFailure) && (forceReflection || Engine.UseReflectionBindFallback))
if ((!result.IsSuccess) && (forceReflection || Engine.UseReflectionBindFallback))
{
var reflectionResult = BindMethodUsingReflection(bindFlags, Target, name, typeArgs, args, bindArgs);
if ((reflectionResult is MethodBindSuccess) || forceReflection)
if ((reflectionResult.IsSuccess) || forceReflection)
{
result = reflectionResult;
}
Expand Down Expand Up @@ -197,7 +197,7 @@ private static MethodBindResult BindMethodCore(Type bindContext, BindingFlags bi
var rawResult = BindMethodRaw(bindFlags, binder, target, bindArgs);

var result = MethodBindResult.Create(name, bindFlags, rawResult, target, args);
if ((result is MethodBindFailure) && !(target is HostType) && target.Type.IsInterface)
if (!result.IsSuccess && !(target is HostType) && target.Type.IsInterface)
{
// binding through interface failed; try base interfaces
foreach (var interfaceType in target.Type.GetInterfaces())
Expand All @@ -206,7 +206,7 @@ private static MethodBindResult BindMethodCore(Type bindContext, BindingFlags bi
rawResult = BindMethodRaw(bindFlags, binder, baseInterfaceTarget, bindArgs);

var baseInterfaceResult = MethodBindResult.Create(name, bindFlags, rawResult, target, args);
if (baseInterfaceResult is MethodBindSuccess)
if (baseInterfaceResult.IsSuccess)
{
return baseInterfaceResult;
}
Expand All @@ -217,7 +217,7 @@ private static MethodBindResult BindMethodCore(Type bindContext, BindingFlags bi
rawResult = BindMethodRaw(bindFlags, binder, objectTarget, bindArgs);

var objectResult = MethodBindResult.Create(name, bindFlags, rawResult, target, args);
if (objectResult is MethodBindSuccess)
if (objectResult.IsSuccess)
{
return objectResult;
}
Expand Down Expand Up @@ -354,11 +354,11 @@ private MethodBindResult BindMethodUsingReflection(BindingFlags bindFlags, HostT
}
catch (AmbiguousMatchException)
{
return new MethodBindFailure(() => new AmbiguousMatchException(MiscHelpers.FormatInvariant("The object has multiple methods named '{0}' that match the specified arguments", name)));
return new MethodBindResult(() => new AmbiguousMatchException(MiscHelpers.FormatInvariant("The object has multiple methods named '{0}' that match the specified arguments", name)));
}
}

return new MethodBindFailure(() => new MissingMethodException(MiscHelpers.FormatInvariant("The object has no method named '{0}' that matches the specified arguments", name)));
return new MethodBindResult(() => new MissingMethodException(MiscHelpers.FormatInvariant("The object has no method named '{0}' that matches the specified arguments", name)));
}

private IEnumerable<MethodInfo> GetReflectionCandidates(BindingFlags bindFlags, HostTarget hostTarget, string name, Type[] typeArgs)
Expand Down Expand Up @@ -437,117 +437,80 @@ internal static long GetCoreBindCount()

#region Nested type: MethodBindResult

private abstract class MethodBindResult
{
public static MethodBindResult Create(string name, BindingFlags bindFlags, object rawResult, HostTarget hostTarget, object[] args)
{
var method = rawResult as MethodInfo;
if (method != null)
{
if (method.IsStatic && !bindFlags.HasFlag(BindingFlags.Static))
{
return new MethodBindFailure(() => new InvalidOperationException(MiscHelpers.FormatInvariant("Cannot access static method '{0}' in non-static context", method.Name)));
}

return new MethodBindSuccess(hostTarget, method, args);
}

return new MethodBindFailure((rawResult as Func<Exception>) ?? (() => new NotSupportedException(MiscHelpers.FormatInvariant("Invocation of method '{0}' failed (unrecognized binding)", name))));
}

public abstract object RawResult { get; }

public abstract bool IsPreferredMethod(HostItem hostItem, string name);

public abstract bool IsUnblockedMethod(HostItem hostItem);

public abstract object Invoke(HostItem hostItem);
}

#endregion

#region Nested type: MethodBindSuccess

private sealed class MethodBindSuccess : MethodBindResult
{
private static readonly MethodInfo[] reflectionMethods =
private readonly ref struct MethodBindResult
{
private readonly HostTarget hostTarget;
private readonly MethodInfo method;
private readonly object[] args;
private readonly Func<Exception> exceptionFactory;

private static readonly MethodInfo[] reflectionMethods =
{
typeof(object).GetMethod("GetType"),
typeof(System.Runtime.InteropServices._Exception).GetMethod("GetType"),
typeof(Exception).GetMethod("GetType")
};

private readonly HostTarget hostTarget;
private readonly MethodInfo method;
private readonly object[] args;

public MethodBindSuccess(HostTarget hostTarget, MethodInfo method, object[] args)
};

public MethodBindResult(HostTarget hostTarget, MethodInfo method, object[] args)
{
this.hostTarget = hostTarget;
this.method = method;
this.args = args;
exceptionFactory = null;
}

public MethodBindResult(Func<Exception> exceptionFactory)
{
hostTarget = null;
method = null;
args = null;
this.exceptionFactory = exceptionFactory;
}

#region MethodBindResult overrides

public override object RawResult => method;

public override bool IsPreferredMethod(HostItem hostItem, string name)
{
return IsUnblockedMethod(hostItem) && (method.GetScriptName(hostItem) == name);
}

public override bool IsUnblockedMethod(HostItem hostItem)
{
return !method.IsBlockedFromScript(hostItem, hostItem.DefaultAccess);
}

public override object Invoke(HostItem hostItem)
public static MethodBindResult Create(string name, BindingFlags bindFlags, object rawResult, HostTarget hostTarget, object[] args)
{
if (reflectionMethods.Contains(method, MemberComparer<MethodInfo>.Instance))
var method = rawResult as MethodInfo;
if (method != null)
{
hostItem.Engine.CheckReflection();
}

return InvokeHelpers.InvokeMethod(hostItem, method, hostTarget.InvokeTarget, args, method.GetScriptMemberFlags(hostItem));
}

#endregion
}
if (method.IsStatic && !bindFlags.HasFlag(BindingFlags.Static))
{
return new MethodBindResult(() => new InvalidOperationException(MiscHelpers.FormatInvariant("Cannot access static method '{0}' in non-static context", method.Name)));
}

#endregion
return new MethodBindResult(hostTarget, method, args);
}

#region Nested type: MethodBindFailure
return new MethodBindResult((rawResult as Func<Exception>) ?? (() => new NotSupportedException(MiscHelpers.FormatInvariant("Invocation of method '{0}' failed (unrecognized binding)", name))));
}

public bool IsSuccess => method != null;

private sealed class MethodBindFailure : MethodBindResult
{
private readonly Func<Exception> exceptionFactory;
public object RawResult => IsSuccess ? (object)method : exceptionFactory;

public MethodBindFailure(Func<Exception> exceptionFactory)
{
this.exceptionFactory = exceptionFactory;
public bool IsPreferredMethod(HostItem hostItem, string name)
{
return IsSuccess && IsUnblockedMethod(hostItem) && (method.GetScriptName(hostItem) == name);
}

#region MethodBindResult overrides

public override object RawResult => exceptionFactory;

public override bool IsPreferredMethod(HostItem hostItem, string name)
{
return false;
public bool IsUnblockedMethod(HostItem hostItem)
{
return IsSuccess && !method.IsBlockedFromScript(hostItem, hostItem.DefaultAccess);
}

public override bool IsUnblockedMethod(HostItem hostItem)
{
return false;
}
public object Invoke(HostItem hostItem)
{
if (!IsSuccess)
{
throw exceptionFactory();
}

if (reflectionMethods.Contains(method, MemberComparer<MethodInfo>.Instance))
{
hostItem.Engine.CheckReflection();
}

public override object Invoke(HostItem hostItem)
{
throw exceptionFactory();
return InvokeHelpers.InvokeMethod(hostItem, method, hostTarget.InvokeTarget, args, method.GetScriptMemberFlags(hostItem));
}

#endregion
}

#endregion
Expand Down