Commit 09809820 authored by Jon Skeet's avatar Jon Skeet

Evil reflection optimisation.

parent 38da52d3
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);
}
......@@ -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);
}
}
// 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(); };
}
}
}
......@@ -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);
}
}
......
......@@ -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));
}
}
......
......@@ -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);
}
}
}
\ No newline at end of file
......@@ -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();
}
}
}
\ No newline at end of file
......@@ -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
......
......@@ -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;
}
......
......@@ -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] {
......
......@@ -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" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment