You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2018/05/08 07:02:29 UTC
ignite git commit: IGNITE-8434 .NET: Fix service proxy generation
Repository: ignite
Updated Branches:
refs/heads/master aca8da93c -> c9106fb0a
IGNITE-8434 .NET: Fix service proxy generation
* Fix proxies on .NET Core
* Fix interface hierarchy handling
This closes #3946
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/c9106fb0
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/c9106fb0
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/c9106fb0
Branch: refs/heads/master
Commit: c9106fb0ac3f109aa07434911e265b1845984780
Parents: aca8da9
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Tue May 8 10:02:20 2018 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Tue May 8 10:02:20 2018 +0300
----------------------------------------------------------------------
.../Services/ServicesTest.cs | 49 ++++++++-------
.../Apache.Ignite.Core.csproj | 1 -
.../Impl/Binary/ReflectionUtils.cs | 50 +++++++++++++--
.../Impl/Services/ServiceMethodHelper.cs | 61 ------------------
.../Impl/Services/ServiceProxyTypeGenerator.cs | 66 +++++++++++++++-----
5 files changed, 122 insertions(+), 105 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/c9106fb0/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
index c6a8e0b..017a580 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
@@ -379,7 +379,7 @@ namespace Apache.Ignite.Core.Tests.Services
}
/// <summary>
- /// Tests the duck typing: proxy interface can be different from actual service interface,
+ /// Tests the duck typing: proxy interface can be different from actual service interface,
/// only called method signature should be compatible.
/// </summary>
[Test]
@@ -389,7 +389,7 @@ namespace Apache.Ignite.Core.Tests.Services
// Deploy locally or to the remote node
var nodeId = (local ? Grid1 : Grid2).GetCluster().GetLocalNode().Id;
-
+
var cluster = Grid1.GetCluster().ForNodeIds(nodeId);
cluster.GetServices().DeployNodeSingleton(SvcName, svc);
@@ -399,7 +399,7 @@ namespace Apache.Ignite.Core.Tests.Services
// NodeId signature is the same as in service
Assert.AreEqual(nodeId, prx.NodeId);
-
+
// Method signature is different from service signature (object -> object), but is compatible.
Assert.AreEqual(15, prx.Method(15));
@@ -434,7 +434,7 @@ namespace Apache.Ignite.Core.Tests.Services
var top = desc.TopologySnapshot;
var prx = Services.GetServiceProxy<ITestIgniteService>(SvcName);
-
+
Assert.AreEqual(1, top.Count);
Assert.AreEqual(prx.NodeId, top.Keys.Single());
Assert.AreEqual(1, top.Values.Single());
@@ -463,7 +463,7 @@ namespace Apache.Ignite.Core.Tests.Services
res = (IBinaryObject) prx.Method(Grid1.GetBinary().ToBinary<IBinaryObject>(obj));
Assert.AreEqual(11, res.Deserialize<BinarizableObject>().Val);
}
-
+
/// <summary>
/// Tests the server binary flag.
/// </summary>
@@ -558,10 +558,10 @@ namespace Apache.Ignite.Core.Tests.Services
MaxPerNodeCount = 2,
TotalCount = 2,
NodeFilter = new NodeFilter { NodeId = Grid1.GetCluster().GetLocalNode().Id },
- Service = binarizable ? new TestIgniteServiceBinarizable { TestProperty = i, ThrowInit = throwInit }
+ Service = binarizable ? new TestIgniteServiceBinarizable { TestProperty = i, ThrowInit = throwInit }
: new TestIgniteServiceSerializable { TestProperty = i, ThrowInit = throwInit }
});
- }
+ }
var deploymentException = Assert.Throws<ServiceDeploymentException>(() => Services.DeployAll(cfgs));
@@ -569,9 +569,9 @@ namespace Apache.Ignite.Core.Tests.Services
Assert.IsNotNull(failedCfgs);
Assert.AreEqual(2, failedCfgs.Count);
- var firstFailedSvc = binarizable ? failedCfgs.ElementAt(0).Service as TestIgniteServiceBinarizable :
+ var firstFailedSvc = binarizable ? failedCfgs.ElementAt(0).Service as TestIgniteServiceBinarizable :
failedCfgs.ElementAt(0).Service as TestIgniteServiceSerializable;
- var secondFailedSvc = binarizable ? failedCfgs.ElementAt(1).Service as TestIgniteServiceBinarizable :
+ var secondFailedSvc = binarizable ? failedCfgs.ElementAt(1).Service as TestIgniteServiceBinarizable :
failedCfgs.ElementAt(1).Service as TestIgniteServiceSerializable;
Assert.IsNotNull(firstFailedSvc);
@@ -680,7 +680,7 @@ namespace Apache.Ignite.Core.Tests.Services
var deploymentException = Assert.Throws<ServiceDeploymentException>(() => deploy(services, svc));
- var text = keepBinary
+ var text = keepBinary
? "Service deployment failed with a binary error. Examine BinaryCause for details."
: "Service deployment failed with an exception. Examine InnerException for details.";
@@ -759,7 +759,7 @@ namespace Apache.Ignite.Core.Tests.Services
var ex = Assert.Throws<ServiceDeploymentException>(() =>
Services.DeployMultiple(SvcName, svc, Grids.Length, 1));
-
+
Assert.IsNotNull(ex.InnerException);
Assert.AreEqual("Expected exception", ex.InnerException.Message);
@@ -858,7 +858,7 @@ namespace Apache.Ignite.Core.Tests.Services
var arr = new [] {10, 11, 12}.Select(x => new PlatformComputeBinarizable {Field = x}).ToArray<object>();
Assert.AreEqual(new[] {11, 12, 13}, svc.testBinarizableCollection(arr)
.OfType<PlatformComputeBinarizable>().Select(x => x.Field).ToArray());
- Assert.AreEqual(new[] {11, 12, 13},
+ Assert.AreEqual(new[] {11, 12, 13},
svc.testBinarizableArray(arr).OfType<PlatformComputeBinarizable>().Select(x => x.Field).ToArray());
// Binary object
@@ -1074,13 +1074,23 @@ namespace Apache.Ignite.Core.Tests.Services
}
/// <summary>
- /// Test service interface for proxying.
+ /// Test base service.
/// </summary>
- public interface ITestIgniteService
+ public interface ITestIgniteServiceBase
{
+ /** */
int TestProperty { get; set; }
/** */
+ object Method(object arg);
+ }
+
+ /// <summary>
+ /// Test service interface for proxying.
+ /// </summary>
+ public interface ITestIgniteService : IService, ITestIgniteServiceBase
+ {
+ /** */
bool Initialized { get; }
/** */
@@ -1096,9 +1106,6 @@ namespace Apache.Ignite.Core.Tests.Services
string LastCallContextName { get; }
/** */
- object Method(object arg);
-
- /** */
object ErrMethod(object arg);
}
@@ -1124,7 +1131,7 @@ namespace Apache.Ignite.Core.Tests.Services
/// Test serializable service.
/// </summary>
[Serializable]
- private class TestIgniteServiceSerializable : IService, ITestIgniteService
+ private class TestIgniteServiceSerializable : ITestIgniteService
{
/** */
[InstanceResource]
@@ -1232,7 +1239,7 @@ namespace Apache.Ignite.Core.Tests.Services
if (context.AffinityKey != null && !(context.AffinityKey is int))
{
var binaryObj = context.AffinityKey as IBinaryObject;
-
+
var key = binaryObj != null
? binaryObj.Deserialize<BinarizableObject>()
: (BinarizableObject) context.AffinityKey;
@@ -1279,7 +1286,7 @@ namespace Apache.Ignite.Core.Tests.Services
public void WriteBinary(IBinaryWriter writer)
{
writer.WriteInt("TestProp", TestProperty);
-
+
if (ThrowOnWrite)
throw new Exception("Expected exception");
}
@@ -1288,7 +1295,7 @@ namespace Apache.Ignite.Core.Tests.Services
public void ReadBinary(IBinaryReader reader)
{
TestProperty = reader.ReadInt("TestProp");
-
+
throw new Exception("Expected exception");
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/c9106fb0/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
index db9ac85..3e8c00f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
@@ -113,7 +113,6 @@
<Compile Include="Impl\DataRegionMetrics.cs" />
<Compile Include="Impl\PersistentStore\PersistentStoreMetrics.cs" />
<Compile Include="Impl\Services\DynamicServiceProxy.cs" />
- <Compile Include="Impl\Services\ServiceMethodHelper.cs" />
<Compile Include="Impl\Services\ServiceProxyFactory.cs" />
<Compile Include="Impl\Services\ServiceProxyTypeGenerator.cs" />
<Compile Include="Impl\Shell.cs" />
http://git-wip-us.apache.org/repos/asf/ignite/blob/c9106fb0/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/ReflectionUtils.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/ReflectionUtils.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/ReflectionUtils.cs
index 9f004ae..6ea9e2f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/ReflectionUtils.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/ReflectionUtils.cs
@@ -20,6 +20,7 @@ namespace Apache.Ignite.Core.Impl.Binary
using System;
using System.Collections.Generic;
using System.Diagnostics;
+ using System.Linq;
using System.Reflection;
/// <summary>
@@ -27,6 +28,13 @@ namespace Apache.Ignite.Core.Impl.Binary
/// </summary>
internal static class ReflectionUtils
{
+ /** */
+ private const BindingFlags BindFlags =
+ BindingFlags.Public |
+ BindingFlags.NonPublic |
+ BindingFlags.Instance |
+ BindingFlags.DeclaredOnly;
+
/// <summary>
/// Gets all fields, including base classes.
/// </summary>
@@ -57,18 +65,48 @@ namespace Apache.Ignite.Core.Impl.Binary
Debug.Assert(type != null);
if (type.IsPrimitive)
+ {
yield break;
+ }
- const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance |
- BindingFlags.DeclaredOnly;
-
- while (type != typeof(object) && type != null)
+ foreach (var t in GetSelfAndBaseTypes(type))
{
- foreach (var fieldInfo in type.GetFields(bindingFlags))
+ foreach (var fieldInfo in t.GetFields(BindFlags))
yield return new KeyValuePair<MemberInfo, Type>(fieldInfo, fieldInfo.FieldType);
- foreach (var propertyInfo in type.GetProperties(bindingFlags))
+ foreach (var propertyInfo in t.GetProperties(BindFlags))
yield return new KeyValuePair<MemberInfo, Type>(propertyInfo, propertyInfo.PropertyType);
+ }
+ }
+
+ /// <summary>
+ /// Gets methods, including base classes.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ public static IEnumerable<MethodInfo> GetMethods(Type type)
+ {
+ Debug.Assert(type != null);
+
+ if (type.IsInterface)
+ {
+ return type.GetInterfaces().Concat(new[] {typeof(object), type})
+ .SelectMany(t => t.GetMethods(BindFlags));
+ }
+
+ return GetSelfAndBaseTypes(type)
+ .SelectMany(t => t.GetMethods(BindFlags));
+ }
+
+ /// <summary>
+ /// Returns full type hierarchy.
+ /// </summary>
+ private static IEnumerable<Type> GetSelfAndBaseTypes(Type type)
+ {
+ Debug.Assert(type != null);
+
+ while (type != typeof(object) && type != null)
+ {
+ yield return type;
type = type.BaseType;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/c9106fb0/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceMethodHelper.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceMethodHelper.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceMethodHelper.cs
deleted file mode 100644
index e6fb2c0..0000000
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceMethodHelper.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-namespace Apache.Ignite.Core.Impl.Services
-{
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Reflection;
-
- /// <summary>
- /// Provides reflection information about types.
- /// This class used by ServiceProxyTypeGenerator and by generated proxy (to initialize static field).
- /// </summary>
- internal static class ServiceMethodHelper
- {
- /// <summary>
- /// Provides information about virtual methods of the type
- /// </summary>
- /// <param name="type">Type to inspect.</param>
- /// <returns>List of virtual methods.</returns>
- public static MethodInfo[] GetVirtualMethods(Type type)
- {
- Debug.Assert(type != null);
- var methods = new List<MethodInfo>();
-
- foreach (var method in type.GetMethods(BindingFlags.Instance | BindingFlags.Public |
- BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
- {
- if (method.IsVirtual)
- methods.Add(method);
- }
-
- if (type.IsInterface)
- {
- foreach (var method in typeof(object).GetMethods(
- BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
- {
- if (method.IsVirtual)
- methods.Add(method);
- }
- }
-
- return methods.ToArray();
- }
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/c9106fb0/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyTypeGenerator.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyTypeGenerator.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyTypeGenerator.cs
index 97de9c7..8c84ccd 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyTypeGenerator.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyTypeGenerator.cs
@@ -19,8 +19,11 @@ namespace Apache.Ignite.Core.Impl.Services
{
using System;
using System.Diagnostics;
+ using System.Linq;
+ using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
+ using Apache.Ignite.Core.Impl.Binary;
using ProxyAction = System.Func<System.Reflection.MethodBase, object[], object>;
/// <summary>
@@ -35,6 +38,26 @@ namespace Apache.Ignite.Core.Impl.Services
private static readonly MethodInfo InvokeMethod = ActionType.GetMethod("Invoke");
/** */
+ private static readonly MethodInfo Finalizer = typeof(object)
+ .GetMethod("Finalize", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ /** Classic .NET Method. */
+ private static readonly MethodInfo AppDomainDefineAssembly = typeof(AppDomain).GetMethod(
+ "DefineDynamicAssembly",
+ BindingFlags.Public | BindingFlags.Instance,
+ null,
+ new[] {typeof(AssemblyName), typeof(AssemblyBuilderAccess)},
+ null);
+
+ /** .NET Core Method. */
+ private static readonly MethodInfo AssemblyBuilderDefineAssembly = typeof(AssemblyBuilder).GetMethod(
+ "DefineDynamicAssembly",
+ BindingFlags.Public | BindingFlags.Static,
+ null,
+ new[] {typeof(AssemblyName), typeof(AssemblyBuilderAccess)},
+ null);
+
+ /** */
private static readonly ModuleBuilder ModuleBuilder = CreateModuleBuilder();
/// <summary>
@@ -59,7 +82,12 @@ namespace Apache.Ignite.Core.Impl.Services
GenerateStaticConstructor(buildContext);
GenerateConstructor(buildContext);
- buildContext.Methods = ServiceMethodHelper.GetVirtualMethods(buildContext.ServiceType);
+ Debug.Assert(Finalizer != null);
+
+ buildContext.Methods = ReflectionUtils.GetMethods(buildContext.ServiceType)
+ .Where(m => m.IsVirtual && m != Finalizer)
+ .ToArray();
+
for (var i = 0; i < buildContext.Methods.Length; i++)
{
GenerateMethod(buildContext, i);
@@ -76,15 +104,21 @@ namespace Apache.Ignite.Core.Impl.Services
{
var name = Guid.NewGuid().ToString("N");
-#if !NETCOREAPP2_0
- var assemblyBuilder =
- AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(name),
- AssemblyBuilderAccess.RunAndCollect);
-#else
- var assemblyBuilder =
- AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(name),
- AssemblyBuilderAccess.RunAndCollect);
-#endif
+ var asmName = Expression.Constant(new AssemblyName(name));
+ var access = Expression.Constant(AssemblyBuilderAccess.RunAndCollect);
+ var domain = Expression.Constant(AppDomain.CurrentDomain);
+
+ // AppDomain.DefineDynamicAssembly is not available on .NET Core;
+ // AssemblyBuilder.DefineDynamicAssembly is not available on .NET 4.
+ // Both of them can not be called with Reflection.
+ // So we have to be creative and use expression trees.
+ var callExpr = AppDomainDefineAssembly != null
+ ? Expression.Call(domain, AppDomainDefineAssembly, asmName, access)
+ : Expression.Call(AssemblyBuilderDefineAssembly, asmName, access);
+
+ var callExprLambda = Expression.Lambda<Func<AssemblyBuilder>>(callExpr);
+
+ var assemblyBuilder = callExprLambda.Compile()();
return assemblyBuilder.DefineDynamicModule(name);
}
@@ -97,7 +131,7 @@ namespace Apache.Ignite.Core.Impl.Services
// Static field - empty object array to optimize calls without parameters.
buildContext.EmptyParametersField = buildContext.ProxyType.DefineField("_emptyParameters",
typeof(object[]), FieldAttributes.Static | FieldAttributes.Private | FieldAttributes.InitOnly);
-
+
// Instance field for function to invoke.
buildContext.ActionField = buildContext.ProxyType.DefineField("_action", ActionType,
FieldAttributes.Private | FieldAttributes.InitOnly);
@@ -197,10 +231,10 @@ namespace Apache.Ignite.Core.Impl.Services
// Load methods array field.
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldfld, buildContext.MethodsField);
-
+
// Load index of method.
gen.Emit(OpCodes.Ldc_I4, methodIndex);
-
+
// Load array element.
gen.Emit(OpCodes.Ldelem_Ref);
@@ -215,10 +249,10 @@ namespace Apache.Ignite.Core.Impl.Services
for (var i = 0; i < parameters.Length; i++)
{
gen.Emit(OpCodes.Dup);
-
+
// Parameter's index in array.
gen.Emit(OpCodes.Ldc_I4, i);
-
+
// Parameter's value.
gen.Emit(OpCodes.Ldarg, i + 1);
if (parameterTypes[i].IsValueType)
@@ -278,4 +312,4 @@ namespace Apache.Ignite.Core.Impl.Services
/** */ public MethodInfo[] Methods { get; set; }
}
}
-}
\ No newline at end of file
+}