diff --git a/src/StreamJsonRpc/SkipClrVisibilityChecks.cs b/src/StreamJsonRpc/SkipClrVisibilityChecks.cs index 273ba0ba..a1c1e013 100644 --- a/src/StreamJsonRpc/SkipClrVisibilityChecks.cs +++ b/src/StreamJsonRpc/SkipClrVisibilityChecks.cs @@ -87,12 +87,15 @@ internal static ImmutableHashSet GetSkipVisibilityChecksRequiremen CheckForNonPublicTypes(typeInfo, assembliesDeclaringInternalTypes, visitedTypes); // Enumerate members on the interface that we're going to need to implement. - foreach (MethodInfo methodInfo in typeInfo.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy)) + foreach (TypeInfo iteratedTypeInfo in ThisAndBaseTypes(typeInfo)) { - CheckForNonPublicTypes(methodInfo.ReturnType.GetTypeInfo(), assembliesDeclaringInternalTypes, visitedTypes); - foreach (ParameterInfo parameter in methodInfo.GetParameters()) + foreach (MethodInfo methodInfo in iteratedTypeInfo.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { - CheckForNonPublicTypes(parameter.ParameterType.GetTypeInfo(), assembliesDeclaringInternalTypes, visitedTypes); + CheckForNonPublicTypes(methodInfo.ReturnType.GetTypeInfo(), assembliesDeclaringInternalTypes, visitedTypes); + foreach (ParameterInfo parameter in methodInfo.GetParameters()) + { + CheckForNonPublicTypes(parameter.ParameterType.GetTypeInfo(), assembliesDeclaringInternalTypes, visitedTypes); + } } } @@ -131,6 +134,25 @@ internal void SkipVisibilityChecksFor(AssemblyName assemblyName) } } + private static IEnumerable ThisAndBaseTypes(TypeInfo startingPoint) + { + if (startingPoint.IsInterface) + { + yield return startingPoint.GetTypeInfo(); + foreach (Type iface in startingPoint.GetInterfaces()) + { + yield return iface.GetTypeInfo(); + } + } + else + { + for (TypeInfo? t = startingPoint.GetTypeInfo(); t is not null && t != typeof(object).GetTypeInfo(); t = t.BaseType?.GetTypeInfo()) + { + yield return t; + } + } + } + private static void CheckForNonPublicTypes(TypeInfo typeInfo, ImmutableHashSet.Builder assembliesDeclaringInternalTypes, HashSet visitedTypes) { Requires.NotNull(typeInfo, nameof(typeInfo)); diff --git a/test/StreamJsonRpc.Tests.ExternalAssembly/IInternalGenericInterface.cs b/test/StreamJsonRpc.Tests.ExternalAssembly/IInternalGenericInterface.cs new file mode 100644 index 00000000..810c2285 --- /dev/null +++ b/test/StreamJsonRpc.Tests.ExternalAssembly/IInternalGenericInterface.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace StreamJsonRpc.Tests.ExternalAssembly +{ + using System.Threading.Tasks; + + internal interface IInternalGenericInterface + { + Task GetOptionsAsync(InternalStruct id, CancellationToken cancellationToken); + } +} diff --git a/test/StreamJsonRpc.Tests.ExternalAssembly/InternalStruct.cs b/test/StreamJsonRpc.Tests.ExternalAssembly/InternalStruct.cs new file mode 100644 index 00000000..7c12e43d --- /dev/null +++ b/test/StreamJsonRpc.Tests.ExternalAssembly/InternalStruct.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace StreamJsonRpc.Tests.ExternalAssembly +{ + internal struct InternalStruct + { + } +} diff --git a/test/StreamJsonRpc.Tests/JsonRpcProxyGenerationTests.cs b/test/StreamJsonRpc.Tests/JsonRpcProxyGenerationTests.cs index 1126ae06..cf790795 100644 --- a/test/StreamJsonRpc.Tests/JsonRpcProxyGenerationTests.cs +++ b/test/StreamJsonRpc.Tests/JsonRpcProxyGenerationTests.cs @@ -1,16 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; using Microsoft; using Microsoft.VisualStudio.Threading; using Nerdbank; using StreamJsonRpc; using Xunit; using Xunit.Abstractions; +using ExAssembly = StreamJsonRpc.Tests.ExternalAssembly; public class JsonRpcProxyGenerationTests : TestBase { @@ -137,14 +134,27 @@ public interface IServerWithGenericMethod Task AddAsync(T a, T b); } - internal interface IServerInternal : StreamJsonRpc.Tests.ExternalAssembly.ISomeInternalProxyInterface, IServerInternalWithInternalTypesFromOtherAssemblies + internal interface IServerInternal : ExAssembly.ISomeInternalProxyInterface, IServerInternalWithInternalTypesFromOtherAssemblies { Task AddAsync(int a, int b); } internal interface IServerInternalWithInternalTypesFromOtherAssemblies { - Task SomeMethodAsync(); + Task SomeMethodAsync(); + } + + internal interface IRemoteService + { + internal interface ICallback : ExAssembly.IInternalGenericInterface + { + } + } + + [Fact] + public void Tomas_Internal() + { + JsonRpc.Attach(new MemoryStream()); } [Fact] @@ -428,7 +438,7 @@ public async Task InternalInterface() var clientRpc = JsonRpc.Attach(streams.Item1); // Try the first internal interface, which is external to this test assembly - var proxy1 = clientRpc.Attach(); + var proxy1 = clientRpc.Attach(); Assert.Equal(-1, await proxy1.SubtractAsync(1, 2).WithCancellation(this.TimeoutToken)); // Now create a proxy for another interface that is internal within this assembly, but derives from the external assembly's internal interface. @@ -815,11 +825,17 @@ internal class ServerOfInternalInterface : IServerInternal { public Task AddAsync(int a, int b) => Task.FromResult(a + b); - public Task SomeMethodAsync() + public Task SomeMethodAsync() { - return Task.FromResult(new StreamJsonRpc.Tests.ExternalAssembly.SomeOtherInternalType()); + return Task.FromResult(new ExAssembly.SomeOtherInternalType()); } public Task SubtractAsync(int a, int b) => Task.FromResult(a - b); } + + internal class Callback : IRemoteService.ICallback + { + public Task GetOptionsAsync(ExAssembly.InternalStruct id, CancellationToken cancellationToken) + => Task.FromResult(null); + } }