Explorar el Código

Evil reflection optimisation.

Jon Skeet hace 17 años
padre
commit
0980982095

+ 20 - 3
csharp/ProtocolBuffers/FieldAccess/Delegates.cs

@@ -1,11 +1,28 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.
+// http://code.google.com/p/protobuf/
+//
+// Licensed 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 Google.ProtocolBuffers.FieldAccess {
 
+  // TODO(jonskeet): Convert these to Func/Action family
   delegate bool HasDelegate<T>(T message);
   delegate T ClearDelegate<T>(T builder);
   delegate int RepeatedCountDelegate<T>(T message);
   delegate object GetValueDelegate<T>(T message);
+  delegate void SingleValueDelegate<TSource>(TSource source, object value);
+  delegate IBuilder CreateBuilderDelegate();
+  delegate object GetIndexedValueDelegate<T>(T message, int index);
+  delegate object SetIndexedValueDelegate<T>(T message, int index, object value);
 }

+ 4 - 4
csharp/ProtocolBuffers/FieldAccess/IFieldAccessor.cs

@@ -58,18 +58,18 @@ namespace Google.ProtocolBuffers.FieldAccess {
     /// <summary>
     /// Accessor for repeated fields
     /// </summary>
-    object GetRepeatedValue(IMessage message, int index);
+    object GetRepeatedValue(TMessage message, int index);
     /// <summary>
     /// Mutator for repeated fields
     /// </summary>
-    void SetRepeated(IBuilder builder, int index, object value);
+    void SetRepeated(TBuilder builder, int index, object value);
     /// <summary>
     /// Adds the specified value to the field in the given builder.
     /// </summary>
-    void AddRepeated(IBuilder builder, object value);
+    void AddRepeated(TBuilder builder, object value);
     /// <summary>
     /// Returns a read-only wrapper around the value of a repeated field.
     /// </summary>
-    object GetRepeatedWrapper(IBuilder builder);
+    object GetRepeatedWrapper(TBuilder builder);
   }
 }

+ 118 - 0
csharp/ProtocolBuffers/FieldAccess/ReflectionUtil.cs

@@ -0,0 +1,118 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.
+// http://code.google.com/p/protobuf/
+//
+// Licensed 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.
+using System;
+using System.Reflection;
+
+namespace Google.ProtocolBuffers.FieldAccess {
+
+  /// <summary>
+  /// The methods in this class are somewhat evil, and should not be tampered with lightly.
+  /// Basically they allow the creation of relatively weakly typed delegates from MethodInfos
+  /// which are more strongly typed. They do this by creating an appropriate strongly typed
+  /// delegate from the MethodInfo, and then calling that within an anonymous method.
+  /// Mind-bending stuff (at least to your humble narrator) but the resulting delegates are
+  /// very fast compared with calling Invoke later on.
+  /// </summary>
+  internal static class ReflectionUtil {
+
+    /// <summary>
+    /// Creates a delegate which will execute the given method and then return
+    /// the result as an object.
+    /// </summary>
+    public static GetValueDelegate<T> CreateUpcastDelegate<T>(MethodInfo method) {
+
+      // The tricky bit is invoking CreateCreateUpcastDelegateImpl with the right type parameters
+      MethodInfo openImpl = typeof(ReflectionUtil).GetMethod("CreateUpcastDelegateImpl");
+      MethodInfo closedImpl = openImpl.MakeGenericMethod(typeof(T), method.ReturnType);
+      return (GetValueDelegate<T>) closedImpl.Invoke(null, new object[] { method });
+    }
+
+    delegate TResult Getter<TSource, TResult>(TSource source);
+
+    /// <summary>
+    /// Method used solely for implementing CreateUpcastDelegate. Public to avoid trust issues
+    /// in low-trust scenarios, e.g. Silverlight.
+    /// TODO(jonskeet): Check any of this actually works in Silverlight...
+    /// </summary>
+    public static GetValueDelegate<TSource> CreateUpcastDelegateImpl<TSource, TResult>(MethodInfo method) {
+      // Convert the reflection call into an open delegate, i.e. instead of calling x.Method()
+      // we'll call getter(x).
+      Getter<TSource, TResult> getter = (Getter<TSource, TResult>)Delegate.CreateDelegate(typeof(Getter<TSource, TResult>), method);
+
+      // Implicit upcast to object (within the delegate)
+      return delegate(TSource source) { return getter(source); };
+    }
+
+
+    /// <summary>
+    /// Creates a delegate which will execute the given method after casting the parameter
+    /// down from object to the required parameter type.
+    /// </summary>
+    public static SingleValueDelegate<T> CreateDowncastDelegate<T>(MethodInfo method) {
+      MethodInfo openImpl = typeof(ReflectionUtil).GetMethod("CreateDowncastDelegateImpl");
+      MethodInfo closedImpl = openImpl.MakeGenericMethod(typeof(T), method.GetParameters()[0].ParameterType);
+      return (SingleValueDelegate<T>)closedImpl.Invoke(null, new object[] { method });
+    }
+
+    delegate void OpenSingleValueDelegate<TSource, TParam>(TSource source, TParam parameter);
+
+    public static SingleValueDelegate<TSource> CreateDowncastDelegateImpl<TSource, TParam>(MethodInfo method) {
+      // Convert the reflection call into an open delegate, i.e. instead of calling x.Method(y) we'll
+      // call Method(x, y)
+      OpenSingleValueDelegate<TSource, TParam> call = (OpenSingleValueDelegate<TSource, TParam>)
+          Delegate.CreateDelegate(typeof(OpenSingleValueDelegate<TSource, TParam>), method);
+
+      return delegate(TSource source, object parameter) { call(source, (TParam)parameter); };
+    }
+
+    /// <summary>
+    /// Creates a delegate which will execute the given method after casting the parameter
+    /// down from object to the required parameter type.
+    /// </summary>
+    public static SingleValueDelegate<T> CreateDowncastDelegateIgnoringReturn<T>(MethodInfo method) {
+      MethodInfo openImpl = typeof(ReflectionUtil).GetMethod("CreateDowncastDelegateIgnoringReturnImpl");
+      MethodInfo closedImpl = openImpl.MakeGenericMethod(typeof(T), method.GetParameters()[0].ParameterType, method.ReturnType);
+      return (SingleValueDelegate<T>)closedImpl.Invoke(null, new object[] { method });
+    }
+
+    delegate TReturn OpenSingleValueDelegate<TSource, TParam, TReturn>(TSource source, TParam parameter);
+
+    public static SingleValueDelegate<TSource> CreateDowncastDelegateIgnoringReturnImpl<TSource, TParam, TReturn>(MethodInfo method) {
+      // Convert the reflection call into an open delegate, i.e. instead of calling x.Method(y) we'll
+      // call Method(x, y)
+      OpenSingleValueDelegate<TSource, TParam, TReturn> call = (OpenSingleValueDelegate<TSource, TParam, TReturn>)
+          Delegate.CreateDelegate(typeof(OpenSingleValueDelegate<TSource, TParam, TReturn>), method);
+
+      return delegate(TSource source, object parameter) { call(source, (TParam)parameter); };
+    }
+
+    delegate T OpenCreateBuilderDelegate<T>();
+
+    /// <summary>
+    /// Creates a delegate which will execute the given static method and cast the result up to IBuilder.
+    /// </summary>
+    public static CreateBuilderDelegate CreateStaticUpcastDelegate(MethodInfo method) {
+      MethodInfo openImpl = typeof(ReflectionUtil).GetMethod("CreateStaticUpcastDelegateImpl");
+      MethodInfo closedImpl = openImpl.MakeGenericMethod(method.ReturnType);
+      return (CreateBuilderDelegate) closedImpl.Invoke(null, new object[] { method });
+    }
+
+    public static CreateBuilderDelegate CreateStaticUpcastDelegateImpl<T>(MethodInfo method) {
+      OpenCreateBuilderDelegate<T> call = (OpenCreateBuilderDelegate<T>)Delegate.CreateDelegate(typeof(OpenCreateBuilderDelegate<T>), method);
+      return delegate { return (IBuilder)call(); };
+    }
+  }
+}

+ 3 - 3
csharp/ProtocolBuffers/FieldAccess/RepeatedEnumAccessor.cs

@@ -42,18 +42,18 @@ namespace Google.ProtocolBuffers.FieldAccess {
       return Lists.AsReadOnly(ret);
     }
 
-    public override object GetRepeatedValue(IMessage message, int index) {
+    public override object GetRepeatedValue(TMessage message, int index) {
       // Note: This relies on the fact that the CLR allows unboxing from an enum to
       // its underlying value
       int rawValue = (int) base.GetRepeatedValue(message, index);
       return enumDescriptor.FindValueByNumber(rawValue);
     }
 
-    public override void AddRepeated(IBuilder builder, object value) {
+    public override void AddRepeated(TBuilder builder, object value) {
       base.AddRepeated(builder, ((EnumValueDescriptor) value).Number);
     }
 
-    public override void SetRepeated(IBuilder builder, int index, object value) {
+    public override void SetRepeated(TBuilder builder, int index, object value) {
       base.SetRepeated(builder, index, ((EnumValueDescriptor) value).Number);
     }
   }

+ 6 - 5
csharp/ProtocolBuffers/FieldAccess/RepeatedMessageAccessor.cs

@@ -33,13 +33,14 @@ namespace Google.ProtocolBuffers.FieldAccess {
     /// in a message type "Foo", a field called "bar" might be of type "Baz". This
     /// method is Baz.CreateBuilder.
     /// </summary>
-    private readonly MethodInfo createBuilderMethod;
+    private readonly CreateBuilderDelegate createBuilderDelegate;
 
     internal RepeatedMessageAccessor(string name) : base(name) {
-      createBuilderMethod = ClrType.GetMethod("CreateBuilder", new Type[0]);
+      MethodInfo createBuilderMethod = ClrType.GetMethod("CreateBuilder", new Type[0]);
       if (createBuilderMethod == null) {
         throw new ArgumentException("No public static CreateBuilder method declared in " + ClrType.Name);
       }
+      createBuilderDelegate = ReflectionUtil.CreateStaticUpcastDelegate(createBuilderMethod);
     }
 
     /// <summary>
@@ -58,15 +59,15 @@ namespace Google.ProtocolBuffers.FieldAccess {
       return CreateBuilder().WeakMergeFrom(message).WeakBuild();
     }
 
-    public override void SetRepeated(IBuilder builder, int index, object value) {
+    public override void SetRepeated(TBuilder builder, int index, object value) {
       base.SetRepeated(builder, index, CoerceType(value));
     }
 
     public override IBuilder CreateBuilder() {
-      return (IBuilder) createBuilderMethod.Invoke(null, null);
+      return createBuilderDelegate();
     }
 
-    public override void AddRepeated(IBuilder builder, object value) {
+    public override void AddRepeated(TBuilder builder, object value) {
       base.AddRepeated(builder, CoerceType(value));
     }
   }

+ 21 - 15
csharp/ProtocolBuffers/FieldAccess/RepeatedPrimitiveAccessor.cs

@@ -25,13 +25,15 @@ namespace Google.ProtocolBuffers.FieldAccess {
       where TMessage : IMessage<TMessage, TBuilder>
       where TBuilder : IBuilder<TMessage, TBuilder> {
 
-    private readonly PropertyInfo messageProperty;
-    private readonly PropertyInfo builderProperty;
-    private readonly RepeatedCountDelegate<TMessage> countDelegate;
+    private readonly Type clrType;
+    private readonly GetValueDelegate<TMessage> getValueDelegate;
     private readonly ClearDelegate<TBuilder> clearDelegate;
-    private readonly MethodInfo addMethod;
+    private readonly SingleValueDelegate<TBuilder> addValueDelegate;
+    private readonly GetValueDelegate<TBuilder> getRepeatedWrapperDelegate;
+    private readonly RepeatedCountDelegate<TMessage> countDelegate;
     private readonly MethodInfo getElementMethod;
     private readonly MethodInfo setElementMethod;
+    
 
     /// <summary>
     /// The CLR type of the field (int, the enum type, ByteString, the message etc).
@@ -39,16 +41,17 @@ namespace Google.ProtocolBuffers.FieldAccess {
     /// value.
     /// </summary>
     protected Type ClrType {
-      get { return getElementMethod.ReturnType; }
+      get { return clrType; }
     }
 
     internal RepeatedPrimitiveAccessor(string name) {      
-      messageProperty = typeof(TMessage).GetProperty(name + "List");
-      builderProperty = typeof(TBuilder).GetProperty(name + "List");
+      PropertyInfo messageProperty = typeof(TMessage).GetProperty(name + "List");
+      PropertyInfo builderProperty = typeof(TBuilder).GetProperty(name + "List");
       PropertyInfo countProperty = typeof(TMessage).GetProperty(name + "Count");
       MethodInfo clearMethod = typeof(TBuilder).GetMethod("Clear" + name);
       getElementMethod = typeof(TMessage).GetMethod("Get" + name, new Type[] { typeof(int) });
-      addMethod = typeof(TBuilder).GetMethod("Add" + name, new Type[] { ClrType });
+      clrType = getElementMethod.ReturnType;
+      MethodInfo addMethod = typeof(TBuilder).GetMethod("Add" + name, new Type[] { ClrType });
       setElementMethod = typeof(TBuilder).GetMethod("Set" + name, new Type[] { typeof(int), ClrType });
       if (messageProperty == null 
           || builderProperty == null 
@@ -62,6 +65,9 @@ namespace Google.ProtocolBuffers.FieldAccess {
       clearDelegate = (ClearDelegate<TBuilder>)Delegate.CreateDelegate(typeof(ClearDelegate<TBuilder>), clearMethod);
       countDelegate = (RepeatedCountDelegate<TMessage>)Delegate.CreateDelegate
           (typeof(RepeatedCountDelegate<TMessage>), countProperty.GetGetMethod());
+      getValueDelegate = ReflectionUtil.CreateUpcastDelegate<TMessage>(messageProperty.GetGetMethod());
+      addValueDelegate = ReflectionUtil.CreateDowncastDelegateIgnoringReturn<TBuilder>(addMethod);
+      getRepeatedWrapperDelegate = ReflectionUtil.CreateUpcastDelegate<TBuilder>(builderProperty.GetGetMethod());
     }
 
     public bool Has(TMessage message) {
@@ -73,7 +79,7 @@ namespace Google.ProtocolBuffers.FieldAccess {
     }
 
     public virtual object GetValue(TMessage message) {
-      return messageProperty.GetValue(message, null);
+      return getValueDelegate(message);
     }
 
     public void SetValue(TBuilder builder, object value) {
@@ -95,24 +101,24 @@ namespace Google.ProtocolBuffers.FieldAccess {
       return countDelegate(message);
     }
 
-    public virtual object GetRepeatedValue(IMessage message, int index) {
+    public virtual object GetRepeatedValue(TMessage message, int index) {
       return getElementMethod.Invoke(message, new object[] {index } );
     }
 
-    public virtual void SetRepeated(IBuilder builder, int index, object value) {
+    public virtual void SetRepeated(TBuilder builder, int index, object value) {
       setElementMethod.Invoke(builder, new object[] {index, value} );
     }
 
-    public virtual void AddRepeated(IBuilder builder, object value) {
-      addMethod.Invoke(builder, new object[] { value });
+    public virtual void AddRepeated(TBuilder builder, object value) {
+      addValueDelegate(builder, value);
     }
 
     /// <summary>
     /// The builder class's accessor already builds a read-only wrapper for
     /// us, which is exactly what we want.
     /// </summary>
-    public object GetRepeatedWrapper(IBuilder builder) {
-      return builderProperty.GetValue(builder, null);
+    public object GetRepeatedWrapper(TBuilder builder) {
+      return getRepeatedWrapperDelegate(builder);
     }
   }
 }

+ 5 - 6
csharp/ProtocolBuffers/FieldAccess/SingleMessageAccessor.cs

@@ -29,15 +29,14 @@ namespace Google.ProtocolBuffers.FieldAccess {
     /// in a message type "Foo", a field called "bar" might be of type "Baz". This
     /// method is Baz.CreateBuilder.
     /// </summary>
-    private readonly MethodInfo createBuilderMethod;
+    private readonly CreateBuilderDelegate createBuilderDelegate;
 
-
-    internal SingleMessageAccessor(string name) : base(name) {
-      
-      createBuilderMethod = ClrType.GetMethod("CreateBuilder", new Type[0]);//BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
+    internal SingleMessageAccessor(string name) : base(name) {      
+      MethodInfo createBuilderMethod = ClrType.GetMethod("CreateBuilder", new Type[0]);
       if (createBuilderMethod == null) {
         throw new ArgumentException("No public static CreateBuilder method declared in " + ClrType.Name);
       }
+      createBuilderDelegate = ReflectionUtil.CreateStaticUpcastDelegate(createBuilderMethod);
     }
 
     /// <summary>
@@ -61,7 +60,7 @@ namespace Google.ProtocolBuffers.FieldAccess {
     }
 
     public override IBuilder CreateBuilder() {
-      return (IBuilder) createBuilderMethod.Invoke(null, null);
+      return createBuilderDelegate();
     }
   }
 }

+ 15 - 11
csharp/ProtocolBuffers/FieldAccess/SinglePrimitiveAccessor.cs

@@ -24,8 +24,9 @@ namespace Google.ProtocolBuffers.FieldAccess {
       where TMessage : IMessage<TMessage, TBuilder>
       where TBuilder : IBuilder<TMessage, TBuilder> {
 
-    private readonly PropertyInfo messageProperty;
-    private readonly PropertyInfo builderProperty;
+    private readonly Type clrType;
+    private readonly GetValueDelegate<TMessage> getValueDelegate;
+    private readonly SingleValueDelegate<TBuilder> setValueDelegate;
     private readonly HasDelegate<TMessage> hasDelegate;
     private readonly ClearDelegate<TBuilder> clearDelegate;
 
@@ -34,19 +35,22 @@ namespace Google.ProtocolBuffers.FieldAccess {
     /// As declared by the property.
     /// </summary>
     protected Type ClrType {
-      get { return messageProperty.PropertyType; }
+      get { return clrType; }
     }
 
     internal SinglePrimitiveAccessor(string name) {
-      messageProperty = typeof(TMessage).GetProperty(name);
-      builderProperty = typeof(TBuilder).GetProperty(name);
+      PropertyInfo messageProperty = typeof(TMessage).GetProperty(name);
+      PropertyInfo builderProperty = typeof(TBuilder).GetProperty(name);
       PropertyInfo hasProperty = typeof(TMessage).GetProperty("Has" + name);
       MethodInfo clearMethod = typeof(TBuilder).GetMethod("Clear" + name);
       if (messageProperty == null || builderProperty == null || hasProperty == null || clearMethod == null) {
         throw new ArgumentException("Not all required properties/methods available");
       }
+      clrType = messageProperty.PropertyType;
       hasDelegate = (HasDelegate<TMessage>)Delegate.CreateDelegate(typeof(HasDelegate<TMessage>), hasProperty.GetGetMethod());
       clearDelegate = (ClearDelegate<TBuilder>)Delegate.CreateDelegate(typeof(ClearDelegate<TBuilder>), clearMethod);
+      getValueDelegate = ReflectionUtil.CreateUpcastDelegate<TMessage>(messageProperty.GetGetMethod());
+      setValueDelegate = ReflectionUtil.CreateDowncastDelegate<TBuilder>(builderProperty.GetSetMethod());
     }
 
     public bool Has(TMessage message) {
@@ -65,11 +69,11 @@ namespace Google.ProtocolBuffers.FieldAccess {
     }
 
     public virtual object GetValue(TMessage message) {
-      return messageProperty.GetValue(message, null);
+      return getValueDelegate(message);
     }
 
     public virtual void SetValue(TBuilder builder, object value) {
-      builderProperty.SetValue(builder, value, null);
+      setValueDelegate(builder, value);
     }
 
     #region Methods only related to repeated values
@@ -77,19 +81,19 @@ namespace Google.ProtocolBuffers.FieldAccess {
       throw new InvalidOperationException();
     }
 
-    public object GetRepeatedValue(IMessage message, int index) {
+    public object GetRepeatedValue(TMessage message, int index) {
       throw new InvalidOperationException();
     }
 
-    public void SetRepeated(IBuilder builder, int index, object value) {
+    public void SetRepeated(TBuilder builder, int index, object value) {
       throw new InvalidOperationException();
     }
 
-    public void AddRepeated(IBuilder builder, object value) {
+    public void AddRepeated(TBuilder builder, object value) {
       throw new InvalidOperationException();
     }
 
-    public object GetRepeatedWrapper(IBuilder builder) {
+    public object GetRepeatedWrapper(TBuilder builder) {
       throw new InvalidOperationException();
     }
     #endregion

+ 3 - 3
csharp/ProtocolBuffers/GeneratedBuilder.cs

@@ -51,7 +51,7 @@ namespace Google.ProtocolBuffers {
         // For repeated fields, the underlying list object is still modifiable at this point.
         // Make sure not to expose the modifiable list to the caller.
         return field.IsRepeated
-          ? InternalFieldAccessors[field].GetRepeatedWrapper(this)
+          ? InternalFieldAccessors[field].GetRepeatedWrapper(ThisBuilder)
           : MessageBeingBuilt[field];
       }
       set {
@@ -92,7 +92,7 @@ namespace Google.ProtocolBuffers {
 
     public override object this[FieldDescriptor field, int index] {
       get { return MessageBeingBuilt[field, index]; }
-      set { InternalFieldAccessors[field].SetRepeated(this, index, value); }
+      set { InternalFieldAccessors[field].SetRepeated(ThisBuilder, index, value); }
     }
 
     public override bool HasField(FieldDescriptor field) {
@@ -144,7 +144,7 @@ namespace Google.ProtocolBuffers {
     }
 
     public override TBuilder AddRepeatedField(FieldDescriptor field, object value) {
-      InternalFieldAccessors[field].AddRepeated(this, value);
+      InternalFieldAccessors[field].AddRepeated(ThisBuilder, value);
       return ThisBuilder;
     }
 

+ 1 - 1
csharp/ProtocolBuffers/GeneratedMessage.cs

@@ -112,7 +112,7 @@ namespace Google.ProtocolBuffers {
     }
 
     public override object this[FieldDescriptor field, int index] {
-      get { return InternalFieldAccessors[field].GetRepeatedValue(this, index); }
+      get { return InternalFieldAccessors[field].GetRepeatedValue(ThisMessage, index); }
     }
 
     public override object this[FieldDescriptor field] {

+ 1 - 0
csharp/ProtocolBuffers/ProtocolBuffers.csproj

@@ -72,6 +72,7 @@
     <Compile Include="ExtensionInfo.cs" />
     <Compile Include="ExtensionRegistry.cs" />
     <Compile Include="FieldAccess\Delegates.cs" />
+    <Compile Include="FieldAccess\ReflectionUtil.cs" />
     <Compile Include="FieldAccess\SingleEnumAccessor.cs" />
     <Compile Include="FieldAccess\SingleMessageAccessor.cs" />
     <Compile Include="FieldAccess\SinglePrimitiveAccessor.cs" />