Skip to content
This repository has been archived by the owner on Sep 25, 2024. It is now read-only.

Commit

Permalink
Removed impromptu-interface in favour of Castle.Core and its DynamicP…
Browse files Browse the repository at this point in the history
…roxy
  • Loading branch information
teneko committed Mar 14, 2021
1 parent 05c6a55 commit f338523
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ public interface IJSObjectReferenceFacade
IJSObjectReference JSObjectReference { get; }

ValueTask<TValue> InvokeAsync<TValue>(string identifier, [Accommodatable] object?[] arguments);
ValueTask<TValue> InvokeAsync<TValue>(string identifier, [Cancellable] CancellationToken cancellationToken, [Accommodatable] object?[] args);
ValueTask<TValue> InvokeAsync<TValue>(string identifier, [Cancellable] TimeSpan timeout, [Accommodatable] object?[] args);
ValueTask<TValue> InvokeAsync<TValue>(string identifier, [Cancellable] CancellationToken cancellationToken, [Accommodatable] object?[] arguments);
ValueTask<TValue> InvokeAsync<TValue>(string identifier, [Cancellable] TimeSpan timeout, [Accommodatable] object?[] arguments);

ValueTask InvokeVoidAsync(string identifier, [Accommodatable] object?[] args);
ValueTask InvokeVoidAsync(string identifier, [Cancellable] CancellationToken cancellationToken, [Accommodatable] object?[] args);
ValueTask InvokeVoidAsync(string identifier, [Cancellable] TimeSpan timeout, [Accommodatable] object?[] args);
ValueTask InvokeVoidAsync(string identifier, [Accommodatable] object?[] arguments);
ValueTask InvokeVoidAsync(string identifier, [Cancellable] CancellationToken cancellationToken, [Accommodatable] object?[] arguments);
ValueTask InvokeVoidAsync(string identifier, [Cancellable] TimeSpan timeout, [Accommodatable] object?[] arguments);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Teronis.Microsoft.JSInterop
namespace Teronis.Microsoft.JSInterop
{
public delegate IJSFunctionalObject GetOrBuildJSFunctionalObjectDelegate();
}
93 changes: 0 additions & 93 deletions src/Microsoft/JSInterop/Dynamic/0/src/JSDynamicObject.cs

This file was deleted.

17 changes: 13 additions & 4 deletions src/Microsoft/JSInterop/Dynamic/0/src/JSDynamicObjectActivator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Reflection;
using ImpromptuInterface;
using Castle.DynamicProxy;
using Microsoft.JSInterop;

namespace Teronis.Microsoft.JSInterop.Dynamic
Expand Down Expand Up @@ -57,9 +57,18 @@ public T CreateInstance<T>(IJSObjectReference jsObjectReference)
where T : class, IJSDynamicObject
{
var mainInterfaceType = typeof(T);
var methods = CreateMethodDictionary(mainInterfaceType, typeof(IJSObjectReferenceFacade));
var jsDynamicObject = new JSDynamicObject(jsObjectReference, methods, getOrBuildJSFunctionalObjectDelegate());
return jsDynamicObject.ActLike<T>(/*other interfaces*/);
var methodDictionary = CreateMethodDictionary(mainInterfaceType); // The idea is to forward all not lookup methods
var jsDynamicObjectProxy = new JSDynamicObjectProxy(jsObjectReference, getOrBuildJSFunctionalObjectDelegate());
var proxyGenerator = new ProxyGenerator();

var jsDynamicObjectInterceptor = new JSDynamicObjectInterceptor(
jsDynamicObjectProxy,
methodDictionary,
getOrBuildJSFunctionalObjectDelegate());

return (T)proxyGenerator.CreateInterfaceProxyWithoutTarget(
mainInterfaceType,
jsDynamicObjectInterceptor);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Castle.DynamicProxy;
using Dynamitey;
using Microsoft.JSInterop;
using DynamiteyDynamic = Dynamitey.Dynamic;

namespace Teronis.Microsoft.JSInterop.Dynamic
{
public class JSDynamicObjectInterceptor : IInterceptor
{
private readonly JSDynamicObjectProxy jsDynamicObjectProxy;
private readonly MethodDictionary methodDictionary;
private readonly IJSFunctionalObject jsFunctionalObject;

internal JSDynamicObjectInterceptor(JSDynamicObjectProxy jsDynamicObjectProxy, MethodDictionary methodDictionary, IJSFunctionalObject jsFunctionalObject)
{
this.jsDynamicObjectProxy = jsDynamicObjectProxy ?? throw new ArgumentNullException(nameof(jsDynamicObjectProxy));
this.methodDictionary = methodDictionary ?? throw new ArgumentNullException(nameof(methodDictionary));
this.jsFunctionalObject = jsFunctionalObject;
}

private static string?[] GetPositionalArgumentNames(int numberOfArguments, IReadOnlyList<string> argumentNames)
{
var numberOfArgumentNames = argumentNames.Count;

var positionalArgumentNames = new string?[numberOfArguments];
var currentArgumentPosition = 0;

for (; currentArgumentPosition < numberOfArguments - numberOfArgumentNames; currentArgumentPosition++) {
positionalArgumentNames[currentArgumentPosition] = null;
}

foreach (var argumentName in argumentNames) {
positionalArgumentNames[currentArgumentPosition] = argumentName;
currentArgumentPosition++;
}

return positionalArgumentNames;
}

public void Intercept(IInvocation invocation) {
var name = invocation.Method.Name;
var arguments = invocation.Arguments ?? new object[0];
var genericParameterTypes = invocation.GenericArguments;
var positionalArgumentNames = invocation.Method.GetParameters().Select(x => x.Name);

if (methodDictionary.TryFindMethod(invocation.Method.Name, positionalArgumentNames, out var method)) {
invocation.ReturnValue = method.Invoke(jsFunctionalObject, jsDynamicObjectProxy.JSObjectReference, genericParameterTypes, arguments);
return; // We have our return value set.
}

invocation.ReturnValue = DynamiteyDynamic.InvokeMember(jsDynamicObjectProxy, InvokeMemberName.Create(name, genericParameterTypes), arguments);
}
}
}
43 changes: 43 additions & 0 deletions src/Microsoft/JSInterop/Dynamic/0/src/JSDynamicObjectProxy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.JSInterop;

namespace Teronis.Microsoft.JSInterop.Dynamic
{
public class JSDynamicObjectProxy : IJSDynamicObject
{
public IJSObjectReference JSObjectReference =>
jsObjectReference;

private readonly IJSObjectReference jsObjectReference;
private readonly IJSFunctionalObject jsFunctionalObject;

internal JSDynamicObjectProxy(IJSObjectReference jsObjectReference, IJSFunctionalObject jsFunctionalObject)
{
this.jsObjectReference = jsObjectReference ?? throw new ArgumentNullException(nameof(jsObjectReference));
this.jsFunctionalObject = jsFunctionalObject;
}

public ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[] arguments) =>
jsFunctionalObject.InvokeAsync<TValue>(jsObjectReference, identifier, arguments);

public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, params object?[] args) =>
jsFunctionalObject.InvokeAsync<TValue>(jsObjectReference, identifier, cancellationToken, args);

public ValueTask<TValue> InvokeAsync<TValue>(string identifier, TimeSpan timeout, params object?[] args) =>
jsFunctionalObject.InvokeAsync<TValue>(jsObjectReference, identifier, timeout, args);

public ValueTask InvokeVoidAsync(string identifier, object?[] args) =>
jsFunctionalObject.InvokeVoidAsync(jsObjectReference, identifier, args);

public ValueTask InvokeVoidAsync(string identifier, CancellationToken cancellationToken, object?[] args) =>
jsFunctionalObject.InvokeVoidAsync(jsObjectReference, identifier, cancellationToken, args);

public ValueTask InvokeVoidAsync(string identifier, TimeSpan timeout, object?[] args) =>
jsFunctionalObject.InvokeVoidAsync(jsObjectReference, identifier, timeout, args);

public ValueTask DisposeAsync() =>
jsObjectReference.DisposeAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="ImpromptuInterface" Version="7.0.1" />
<PackageReference Include="Castle.Core" Version="4.4.1" />
<PackageReference Include="Dynamitey" Version="2.0.10.189" />
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
<PackageReference Include="Microsoft.JSInterop" Version="5.0.3" />
</ItemGroup>
Expand Down
31 changes: 15 additions & 16 deletions src/Microsoft/JSInterop/Dynamic/0/test/0/JSDynamicObjectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public JSDynamicObjectTests() =>
JSDynamicObjectActivator = new JSDynamicObjectActivator();

[Fact]
public async Task Should_dispose()
public async Task Proxy_Dispose()
{
// Arrange
var jsObjectReference = new IdentifierPromisingObjectReference();
Expand All @@ -31,23 +31,23 @@ public async Task Should_dispose()
}

[Fact]
public async Task Should_expect_equal_input_output()
public async Task Proxy_Expect_equal_input_output()
{
// Arrange
var jsObjectReference = new JSArgumentsPromisingObjectReference();
var emptyDynamicObject = JSDynamicObjectActivator.CreateInstance<IEmptyDynamicObject>(jsObjectReference);

// Act
var expectedContent = nameof(Should_expect_equal_input_output);
// The extension methods get precedence over dynamic object method calls.
var resultedArguments = await emptyDynamicObject.InvokeAsync<object[]>(expectedContent);
var expectedContent = nameof(Proxy_Expect_equal_input_output);
// The extension methods get precedence over target method calls.
var resultedArguments = await emptyDynamicObject.InvokeAsync<object[]>(identifier: string.Empty, expectedContent);

// Assert
Assert.Equal(new object[] { expectedContent }, resultedArguments);
}

[Fact]
public void Should_throw_not_of_type_value_task_exception()
public void Activation_Throw_not_of_type_value_task_exception()
{
// Arrange
var jsObjectReference = new JSEmptyObjectReference();
Expand All @@ -58,7 +58,7 @@ public void Should_throw_not_of_type_value_task_exception()
}

[Fact]
public async Task Should_expect_identifier_equal_invoked_method()
public async Task Dynamic_Expect_identifier_equal_invoked_method()
{
// Arrange
var jsObjectReference = new IdentifierPromisingObjectReference();
Expand All @@ -73,7 +73,7 @@ public async Task Should_expect_identifier_equal_invoked_method()
}

[Fact]
public async Task Should_throw_token_cancellation()
public async Task Proxy_Throw_token_cancellation()
{
// Arrange
var jsObjectReference = new CancellableObjectReference();
Expand All @@ -86,8 +86,7 @@ public async Task Should_throw_token_cancellation()

// Assert
await Assert.ThrowsAsync<ObjectReferenceInvocationCanceledException>(async () =>
//await jsDynamicObject.InvokeAsync<string>(nameof(Should_expect_cancellation_through_cancellation_token), cancellationToken, args: null));
await jsDynamicObject.InvokeAsync<string>(nameof(Should_throw_token_cancellation), cancellationToken));
await jsDynamicObject.InvokeAsync<string>(nameof(Proxy_Throw_token_cancellation), cancellationToken));
}

public async Task AssertCancellableObjectIsCancelledAsync<SecondArgumentType>(
Expand All @@ -114,7 +113,7 @@ await getCallback(jsDynamicObject)(
}

[Fact]
public async Task Should_throw_token_cancellation_via_annotation()
public async Task Dynamic_Throw_token_cancellation_via_annotation()
{
// Arrange
using var cancellationTokenSource = new CancellationTokenSource();
Expand All @@ -126,7 +125,7 @@ public async Task Should_throw_token_cancellation_via_annotation()
}

[Fact]
public async Task Should_throw_timeout_cancellation_via_annotation()
public async Task Dynamic_Throw_timeout_cancellation_via_annotation()
{
// Arrange
var timeout = TimeSpan.Zero;
Expand All @@ -136,7 +135,7 @@ public async Task Should_throw_timeout_cancellation_via_annotation()
}

[Fact]
public async Task Should_expect_accommodated_arguments()
public async Task Dynamic_Expect_accommodated_arguments()
{
// Arrange
var jsObjectReference = new JSArgumentsPromisingObjectReference();
Expand All @@ -154,7 +153,7 @@ public async Task Should_expect_accommodated_arguments()
}

[Fact]
public void Should_throw_parameter_after_accommodatable_annotated_parameter_exception()
public void Activation_Throw_parameter_after_accommodatable_annotated_parameter_exception()
{
// Arrange
var jsObjectReference = new JSEmptyObjectReference();
Expand All @@ -164,7 +163,7 @@ public void Should_throw_parameter_after_accommodatable_annotated_parameter_exce
}

[Fact]
public void Should_throw_too_many_cancellable_annotated_parameter_exception()
public void Activation_Throw_too_many_cancellable_annotated_parameter_exception()
{
// Arrange
var jsObjectReference = new JSEmptyObjectReference();
Expand All @@ -174,7 +173,7 @@ public void Should_throw_too_many_cancellable_annotated_parameter_exception()
}

[Fact]
public async Task Should_get_identifier_via_annotation()
public async Task Dynamic_Get_identifier_via_annotation()
{
// Arrange
var jsObjectReference = new IdentifierPromisingObjectReference();
Expand Down

0 comments on commit f338523

Please sign in to comment.