Files
2026-08-27 10:56:38 -06:00

1340 lines
30 KiB
C#

using System;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Threading;
using ProtoBuf.Meta;
using ProtoBuf.Serializers;
namespace ProtoBuf.Compiler;
internal sealed class CompilerContext
{
private sealed class UsingBlock : IDisposable
{
private Local local;
private CompilerContext ctx;
private CodeLabel label;
public UsingBlock(CompilerContext ctx, Local local)
{
if (ctx == null)
{
throw new ArgumentNullException("ctx");
}
if (local == null)
{
throw new ArgumentNullException("local");
}
Type type = local.Type;
if ((!Helpers.IsValueType(type) && !Helpers.IsSealed(type)) || ctx.MapType(typeof(IDisposable)).IsAssignableFrom(type))
{
this.local = local;
this.ctx = ctx;
label = ctx.BeginTry();
}
}
public void Dispose()
{
if (this.local == null || ctx == null)
{
return;
}
ctx.EndTry(label, @short: false);
ctx.BeginFinally();
Type type = ctx.MapType(typeof(IDisposable));
MethodInfo method = type.GetMethod("Dispose");
Type type2 = this.local.Type;
if (Helpers.IsValueType(type2))
{
ctx.LoadAddress(this.local, type2);
if (ctx.MetadataVersion == ILVersion.Net1)
{
ctx.LoadValue(this.local);
ctx.CastToObject(type2);
}
else
{
ctx.Constrain(type2);
}
ctx.EmitCall(method);
}
else
{
CodeLabel codeLabel = ctx.DefineLabel();
if (type.IsAssignableFrom(type2))
{
ctx.LoadValue(this.local);
ctx.BranchIfFalse(codeLabel, @short: true);
ctx.LoadAddress(this.local, type2);
}
else
{
using Local local = new Local(ctx, type);
ctx.LoadValue(this.local);
ctx.TryCast(type);
ctx.CopyValue();
ctx.StoreValue(local);
ctx.BranchIfFalse(codeLabel, @short: true);
ctx.LoadAddress(local, type);
}
ctx.EmitCall(method);
ctx.MarkLabel(codeLabel);
}
ctx.EndFinally();
this.local = null;
ctx = null;
label = default(CodeLabel);
}
}
public enum ILVersion
{
Net1,
Net2
}
private readonly DynamicMethod method;
private static int next;
private readonly bool isStatic;
private readonly RuntimeTypeModel.SerializerPair[] methodPairs;
private readonly bool isWriter;
private readonly bool nonPublic;
private readonly Local inputValue;
private readonly string assemblyName;
private readonly ILGenerator il;
private MutableList locals = new MutableList();
private int nextLabel;
private BasicList knownTrustedAssemblies;
private BasicList knownUntrustedAssemblies;
private readonly TypeModel model;
private readonly ILVersion metadataVersion;
public TypeModel Model => model;
internal bool NonPublic => nonPublic;
public Local InputValue => inputValue;
public ILVersion MetadataVersion => metadataVersion;
internal CodeLabel DefineLabel()
{
CodeLabel result = new CodeLabel(il.DefineLabel(), nextLabel++);
return result;
}
[Conditional("DEBUG_COMPILE")]
private void TraceCompile(string value)
{
}
internal void MarkLabel(CodeLabel label)
{
il.MarkLabel(label.Value);
}
public static ProtoSerializer BuildSerializer(IProtoSerializer head, TypeModel model)
{
Type expectedType = head.ExpectedType;
try
{
CompilerContext compilerContext = new CompilerContext(expectedType, isWriter: true, isStatic: true, model, typeof(object));
compilerContext.LoadValue(compilerContext.InputValue);
compilerContext.CastFromObject(expectedType);
compilerContext.WriteNullCheckedTail(expectedType, head, null);
compilerContext.Emit(OpCodes.Ret);
return (ProtoSerializer)compilerContext.method.CreateDelegate(typeof(ProtoSerializer));
}
catch (Exception innerException)
{
string text = expectedType.FullName;
if (string.IsNullOrEmpty(text))
{
text = expectedType.Name;
}
throw new InvalidOperationException("It was not possible to prepare a serializer for: " + text, innerException);
}
}
public static ProtoDeserializer BuildDeserializer(IProtoSerializer head, TypeModel model)
{
Type expectedType = head.ExpectedType;
CompilerContext compilerContext = new CompilerContext(expectedType, isWriter: false, isStatic: true, model, typeof(object));
using (Local local = new Local(compilerContext, expectedType))
{
if (!Helpers.IsValueType(expectedType))
{
compilerContext.LoadValue(compilerContext.InputValue);
compilerContext.CastFromObject(expectedType);
compilerContext.StoreValue(local);
}
else
{
compilerContext.LoadValue(compilerContext.InputValue);
CodeLabel label = compilerContext.DefineLabel();
CodeLabel label2 = compilerContext.DefineLabel();
compilerContext.BranchIfTrue(label, @short: true);
compilerContext.LoadAddress(local, expectedType);
compilerContext.EmitCtor(expectedType);
compilerContext.Branch(label2, @short: true);
compilerContext.MarkLabel(label);
compilerContext.LoadValue(compilerContext.InputValue);
compilerContext.CastFromObject(expectedType);
compilerContext.StoreValue(local);
compilerContext.MarkLabel(label2);
}
head.EmitRead(compilerContext, local);
if (head.ReturnsValue)
{
compilerContext.StoreValue(local);
}
compilerContext.LoadValue(local);
compilerContext.CastToObject(expectedType);
}
compilerContext.Emit(OpCodes.Ret);
return (ProtoDeserializer)compilerContext.method.CreateDelegate(typeof(ProtoDeserializer));
}
internal void Return()
{
Emit(OpCodes.Ret);
}
private static bool IsObject(Type type)
{
return (object)type == typeof(object);
}
internal void CastToObject(Type type)
{
if (!IsObject(type))
{
if (Helpers.IsValueType(type))
{
il.Emit(OpCodes.Box, type);
}
else
{
il.Emit(OpCodes.Castclass, MapType(typeof(object)));
}
}
}
internal void CastFromObject(Type type)
{
if (IsObject(type))
{
return;
}
if (Helpers.IsValueType(type))
{
if (MetadataVersion == ILVersion.Net1)
{
il.Emit(OpCodes.Unbox, type);
il.Emit(OpCodes.Ldobj, type);
}
else
{
il.Emit(OpCodes.Unbox_Any, type);
}
}
else
{
il.Emit(OpCodes.Castclass, type);
}
}
internal MethodBuilder GetDedicatedMethod(int metaKey, bool read)
{
if (methodPairs == null)
{
return null;
}
for (int i = 0; i < methodPairs.Length; i++)
{
if (methodPairs[i].MetaKey == metaKey)
{
if (!read)
{
return methodPairs[i].Serialize;
}
return methodPairs[i].Deserialize;
}
}
throw new ArgumentException("Meta-key not found", "metaKey");
}
internal int MapMetaKeyToCompiledKey(int metaKey)
{
if (metaKey < 0 || methodPairs == null)
{
return metaKey;
}
for (int i = 0; i < methodPairs.Length; i++)
{
if (methodPairs[i].MetaKey == metaKey)
{
return i;
}
}
throw new ArgumentException("Key could not be mapped: " + metaKey, "metaKey");
}
internal CompilerContext(ILGenerator il, bool isStatic, bool isWriter, RuntimeTypeModel.SerializerPair[] methodPairs, TypeModel model, ILVersion metadataVersion, string assemblyName, Type inputType, string traceName)
{
if (string.IsNullOrEmpty(assemblyName))
{
throw new ArgumentNullException("assemblyName");
}
this.assemblyName = assemblyName;
this.isStatic = isStatic;
this.methodPairs = methodPairs ?? throw new ArgumentNullException("methodPairs");
this.il = il ?? throw new ArgumentNullException("il");
this.isWriter = isWriter;
this.model = model ?? throw new ArgumentNullException("model");
this.metadataVersion = metadataVersion;
if ((object)inputType != null)
{
inputValue = new Local(null, inputType);
}
}
private CompilerContext(Type associatedType, bool isWriter, bool isStatic, TypeModel model, Type inputType)
{
metadataVersion = ILVersion.Net2;
this.isStatic = isStatic;
this.isWriter = isWriter;
this.model = model ?? throw new ArgumentNullException("model");
nonPublic = true;
Type typeFromHandle;
Type[] parameterTypes;
if (isWriter)
{
typeFromHandle = typeof(void);
parameterTypes = new Type[2]
{
typeof(object),
typeof(ProtoWriter)
};
}
else
{
typeFromHandle = typeof(object);
parameterTypes = new Type[2]
{
typeof(object),
typeof(ProtoReader)
};
}
method = new DynamicMethod("proto_" + Interlocked.Increment(ref next), typeFromHandle, parameterTypes, associatedType.IsInterface ? typeof(object) : associatedType, skipVisibility: true);
il = method.GetILGenerator();
if ((object)inputType != null)
{
inputValue = new Local(null, inputType);
}
}
private void Emit(OpCode opcode)
{
il.Emit(opcode);
}
public void LoadValue(string value)
{
if (value == null)
{
LoadNullRef();
}
else
{
il.Emit(OpCodes.Ldstr, value);
}
}
public void LoadValue(float value)
{
il.Emit(OpCodes.Ldc_R4, value);
}
public void LoadValue(double value)
{
il.Emit(OpCodes.Ldc_R8, value);
}
public void LoadValue(long value)
{
il.Emit(OpCodes.Ldc_I8, value);
}
public void LoadValue(int value)
{
switch (value)
{
case 0:
Emit(OpCodes.Ldc_I4_0);
return;
case 1:
Emit(OpCodes.Ldc_I4_1);
return;
case 2:
Emit(OpCodes.Ldc_I4_2);
return;
case 3:
Emit(OpCodes.Ldc_I4_3);
return;
case 4:
Emit(OpCodes.Ldc_I4_4);
return;
case 5:
Emit(OpCodes.Ldc_I4_5);
return;
case 6:
Emit(OpCodes.Ldc_I4_6);
return;
case 7:
Emit(OpCodes.Ldc_I4_7);
return;
case 8:
Emit(OpCodes.Ldc_I4_8);
return;
case -1:
Emit(OpCodes.Ldc_I4_M1);
return;
}
if (value >= -128 && value <= 127)
{
il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
}
else
{
il.Emit(OpCodes.Ldc_I4, value);
}
}
internal LocalBuilder GetFromPool(Type type)
{
int count = locals.Count;
for (int i = 0; i < count; i++)
{
LocalBuilder localBuilder = (LocalBuilder)locals[i];
if (localBuilder != null && (object)localBuilder.LocalType == type)
{
locals[i] = null;
return localBuilder;
}
}
return il.DeclareLocal(type);
}
internal void ReleaseToPool(LocalBuilder value)
{
int count = locals.Count;
for (int i = 0; i < count; i++)
{
if (locals[i] == null)
{
locals[i] = value;
return;
}
}
locals.Add(value);
}
public void LoadReaderWriter()
{
Emit(isStatic ? OpCodes.Ldarg_1 : OpCodes.Ldarg_2);
}
public void StoreValue(Local local)
{
if (local == InputValue)
{
byte arg = ((!isStatic) ? ((byte)1) : ((byte)0));
il.Emit(OpCodes.Starg_S, arg);
return;
}
switch (local.Value.LocalIndex)
{
case 0:
Emit(OpCodes.Stloc_0);
break;
case 1:
Emit(OpCodes.Stloc_1);
break;
case 2:
Emit(OpCodes.Stloc_2);
break;
case 3:
Emit(OpCodes.Stloc_3);
break;
default:
{
OpCode opcode = (UseShortForm(local) ? OpCodes.Stloc_S : OpCodes.Stloc);
il.Emit(opcode, local.Value);
break;
}
}
}
public void LoadValue(Local local)
{
if (local == null)
{
return;
}
if (local == InputValue)
{
Emit(isStatic ? OpCodes.Ldarg_0 : OpCodes.Ldarg_1);
return;
}
switch (local.Value.LocalIndex)
{
case 0:
Emit(OpCodes.Ldloc_0);
break;
case 1:
Emit(OpCodes.Ldloc_1);
break;
case 2:
Emit(OpCodes.Ldloc_2);
break;
case 3:
Emit(OpCodes.Ldloc_3);
break;
default:
{
OpCode opcode = (UseShortForm(local) ? OpCodes.Ldloc_S : OpCodes.Ldloc);
il.Emit(opcode, local.Value);
break;
}
}
}
public Local GetLocalWithValue(Type type, Local fromValue)
{
if (fromValue != null)
{
if ((object)fromValue.Type == type)
{
return fromValue.AsCopy();
}
LoadValue(fromValue);
if (!Helpers.IsValueType(type) && ((object)fromValue.Type == null || !type.IsAssignableFrom(fromValue.Type)))
{
Cast(type);
}
}
Local local = new Local(this, type);
StoreValue(local);
return local;
}
internal void EmitBasicRead(string methodName, Type expectedType)
{
MethodInfo methodInfo = MapType(typeof(ProtoReader)).GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if ((object)methodInfo == null || (object)methodInfo.ReturnType != expectedType || methodInfo.GetParameters().Length != 0)
{
throw new ArgumentException("methodName");
}
LoadReaderWriter();
EmitCall(methodInfo);
}
internal void EmitBasicRead(Type helperType, string methodName, Type expectedType)
{
MethodInfo methodInfo = helperType.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if ((object)methodInfo == null || (object)methodInfo.ReturnType != expectedType || methodInfo.GetParameters().Length != 1)
{
throw new ArgumentException("methodName");
}
LoadReaderWriter();
EmitCall(methodInfo);
}
internal void EmitBasicWrite(string methodName, Local fromValue)
{
if (string.IsNullOrEmpty(methodName))
{
throw new ArgumentNullException("methodName");
}
LoadValue(fromValue);
LoadReaderWriter();
EmitCall(GetWriterMethod(methodName));
}
private MethodInfo GetWriterMethod(string methodName)
{
Type type = MapType(typeof(ProtoWriter));
MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
MethodInfo[] array = methods;
foreach (MethodInfo methodInfo in array)
{
if (!(methodInfo.Name != methodName))
{
ParameterInfo[] parameters = methodInfo.GetParameters();
if (parameters.Length == 2 && (object)parameters[1].ParameterType == type)
{
return methodInfo;
}
}
}
throw new ArgumentException("No suitable method found for: " + methodName, "methodName");
}
internal void EmitWrite(Type helperType, string methodName, Local valueFrom)
{
if (string.IsNullOrEmpty(methodName))
{
throw new ArgumentNullException("methodName");
}
MethodInfo methodInfo = helperType.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if ((object)methodInfo == null || (object)methodInfo.ReturnType != MapType(typeof(void)))
{
throw new ArgumentException("methodName");
}
LoadValue(valueFrom);
LoadReaderWriter();
EmitCall(methodInfo);
}
public void EmitCall(MethodInfo method)
{
EmitCall(method, null);
}
public void EmitCall(MethodInfo method, Type targetType)
{
MemberInfo member = method;
CheckAccessibility(ref member);
OpCode opcode;
if (method.IsStatic || Helpers.IsValueType(method.DeclaringType))
{
opcode = OpCodes.Call;
}
else
{
opcode = OpCodes.Callvirt;
if ((object)targetType != null && Helpers.IsValueType(targetType) && !Helpers.IsValueType(method.DeclaringType))
{
Constrain(targetType);
}
}
il.EmitCall(opcode, method, null);
}
public void LoadNullRef()
{
Emit(OpCodes.Ldnull);
}
internal void WriteNullCheckedTail(Type type, IProtoSerializer tail, Local valueFrom)
{
if (Helpers.IsValueType(type))
{
Type underlyingType = Helpers.GetUnderlyingType(type);
if ((object)underlyingType != null)
{
using (Local local = GetLocalWithValue(type, valueFrom))
{
LoadAddress(local, type);
LoadValue(type.GetProperty("HasValue"));
CodeLabel label = DefineLabel();
BranchIfFalse(label, @short: false);
LoadAddress(local, type);
EmitCall(type.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
tail.EmitWrite(this, null);
MarkLabel(label);
return;
}
}
tail.EmitWrite(this, valueFrom);
}
else
{
LoadValue(valueFrom);
CopyValue();
CodeLabel label2 = DefineLabel();
CodeLabel label3 = DefineLabel();
BranchIfTrue(label2, @short: true);
DiscardValue();
Branch(label3, @short: false);
MarkLabel(label2);
tail.EmitWrite(this, null);
MarkLabel(label3);
}
}
internal void ReadNullCheckedTail(Type type, IProtoSerializer tail, Local valueFrom)
{
Type underlyingType;
if (Helpers.IsValueType(type) && (object)(underlyingType = Helpers.GetUnderlyingType(type)) != null)
{
if (tail.RequiresOldValue)
{
using Local local = GetLocalWithValue(type, valueFrom);
LoadAddress(local, type);
EmitCall(type.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
}
tail.EmitRead(this, null);
if (tail.ReturnsValue)
{
EmitCtor(type, underlyingType);
}
}
else
{
tail.EmitRead(this, valueFrom);
}
}
public void EmitCtor(Type type)
{
EmitCtor(type, Helpers.EmptyTypes);
}
public void EmitCtor(ConstructorInfo ctor)
{
if ((object)ctor == null)
{
throw new ArgumentNullException("ctor");
}
MemberInfo member = ctor;
CheckAccessibility(ref member);
il.Emit(OpCodes.Newobj, ctor);
}
public void InitLocal(Type type, Local target)
{
LoadAddress(target, type, evenIfClass: true);
il.Emit(OpCodes.Initobj, type);
}
public void EmitCtor(Type type, params Type[] parameterTypes)
{
if (Helpers.IsValueType(type) && parameterTypes.Length == 0)
{
il.Emit(OpCodes.Initobj, type);
return;
}
ConstructorInfo constructor = Helpers.GetConstructor(type, parameterTypes, nonPublic: true);
if ((object)constructor == null)
{
throw new InvalidOperationException("No suitable constructor found for " + type.FullName);
}
EmitCtor(constructor);
}
private bool InternalsVisible(Assembly assembly)
{
if (string.IsNullOrEmpty(assemblyName))
{
return false;
}
if (knownTrustedAssemblies != null && knownTrustedAssemblies.IndexOfReference(assembly) >= 0)
{
return true;
}
if (knownUntrustedAssemblies != null && knownUntrustedAssemblies.IndexOfReference(assembly) >= 0)
{
return false;
}
bool flag = false;
Type type = MapType(typeof(InternalsVisibleToAttribute));
if ((object)type == null)
{
return false;
}
object[] customAttributes = assembly.GetCustomAttributes(type, inherit: false);
for (int i = 0; i < customAttributes.Length; i++)
{
InternalsVisibleToAttribute internalsVisibleToAttribute = (InternalsVisibleToAttribute)customAttributes[i];
if (internalsVisibleToAttribute.AssemblyName == assemblyName || internalsVisibleToAttribute.AssemblyName.StartsWith(assemblyName + ","))
{
flag = true;
break;
}
}
if (flag)
{
if (knownTrustedAssemblies == null)
{
knownTrustedAssemblies = new BasicList();
}
knownTrustedAssemblies.Add(assembly);
}
else
{
if (knownUntrustedAssemblies == null)
{
knownUntrustedAssemblies = new BasicList();
}
knownUntrustedAssemblies.Add(assembly);
}
return flag;
}
internal void CheckAccessibility(ref MemberInfo member)
{
if ((object)member == null)
{
throw new ArgumentNullException("member");
}
if (NonPublic)
{
return;
}
if (member is FieldInfo && (member.Name.StartsWith("<") & member.Name.EndsWith(">k__BackingField")))
{
string name = member.Name.Substring(1, member.Name.Length - 17);
PropertyInfo property = member.DeclaringType.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
if ((object)property != null)
{
member = property;
}
}
MemberTypes memberType = member.MemberType;
bool flag;
switch (memberType)
{
case MemberTypes.TypeInfo:
{
Type type = (Type)member;
flag = type.IsPublic || InternalsVisible(type.Assembly);
break;
}
case MemberTypes.NestedType:
{
Type type = (Type)member;
do
{
flag = type.IsNestedPublic || type.IsPublic || (((object)type.DeclaringType == null || type.IsNestedAssembly || type.IsNestedFamORAssem) && InternalsVisible(type.Assembly));
}
while (flag && (object)(type = type.DeclaringType) != null);
break;
}
case MemberTypes.Field:
{
FieldInfo fieldInfo = (FieldInfo)member;
flag = fieldInfo.IsPublic || ((fieldInfo.IsAssembly || fieldInfo.IsFamilyOrAssembly) && InternalsVisible(fieldInfo.DeclaringType.Assembly));
break;
}
case MemberTypes.Constructor:
{
ConstructorInfo constructorInfo = (ConstructorInfo)member;
flag = constructorInfo.IsPublic || ((constructorInfo.IsAssembly || constructorInfo.IsFamilyOrAssembly) && InternalsVisible(constructorInfo.DeclaringType.Assembly));
break;
}
case MemberTypes.Method:
{
MethodInfo methodInfo = (MethodInfo)member;
flag = methodInfo.IsPublic || ((methodInfo.IsAssembly || methodInfo.IsFamilyOrAssembly) && InternalsVisible(methodInfo.DeclaringType.Assembly));
if (!flag && (member is MethodBuilder || (object)member.DeclaringType == MapType(typeof(TypeModel))))
{
flag = true;
}
break;
}
case MemberTypes.Property:
flag = true;
break;
default:
throw new NotSupportedException(memberType.ToString());
}
if (!flag)
{
if (memberType == MemberTypes.TypeInfo || memberType == MemberTypes.NestedType)
{
throw new InvalidOperationException("Non-public type cannot be used with full dll compilation: " + ((Type)member).FullName);
}
throw new InvalidOperationException("Non-public member cannot be used with full dll compilation: " + member.DeclaringType.FullName + "." + member.Name);
}
}
public void LoadValue(FieldInfo field)
{
MemberInfo member = field;
CheckAccessibility(ref member);
if (member is PropertyInfo)
{
LoadValue((PropertyInfo)member);
return;
}
OpCode opcode = (field.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld);
il.Emit(opcode, field);
}
public void StoreValue(FieldInfo field)
{
MemberInfo member = field;
CheckAccessibility(ref member);
if (member is PropertyInfo)
{
StoreValue((PropertyInfo)member);
return;
}
OpCode opcode = (field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld);
il.Emit(opcode, field);
}
public void LoadValue(PropertyInfo property)
{
MemberInfo member = property;
CheckAccessibility(ref member);
EmitCall(Helpers.GetGetMethod(property, nonPublic: true, allowInternal: true));
}
public void StoreValue(PropertyInfo property)
{
MemberInfo member = property;
CheckAccessibility(ref member);
EmitCall(Helpers.GetSetMethod(property, nonPublic: true, allowInternal: true));
}
internal static void LoadValue(ILGenerator il, int value)
{
switch (value)
{
case 0:
il.Emit(OpCodes.Ldc_I4_0);
break;
case 1:
il.Emit(OpCodes.Ldc_I4_1);
break;
case 2:
il.Emit(OpCodes.Ldc_I4_2);
break;
case 3:
il.Emit(OpCodes.Ldc_I4_3);
break;
case 4:
il.Emit(OpCodes.Ldc_I4_4);
break;
case 5:
il.Emit(OpCodes.Ldc_I4_5);
break;
case 6:
il.Emit(OpCodes.Ldc_I4_6);
break;
case 7:
il.Emit(OpCodes.Ldc_I4_7);
break;
case 8:
il.Emit(OpCodes.Ldc_I4_8);
break;
case -1:
il.Emit(OpCodes.Ldc_I4_M1);
break;
default:
il.Emit(OpCodes.Ldc_I4, value);
break;
}
}
private bool UseShortForm(Local local)
{
return local.Value.LocalIndex < 256;
}
internal void LoadAddress(Local local, Type type, bool evenIfClass = false)
{
if (evenIfClass || Helpers.IsValueType(type))
{
if (local == null)
{
throw new InvalidOperationException("Cannot load the address of the head of the stack");
}
if (local == InputValue)
{
il.Emit(OpCodes.Ldarga_S, (!isStatic) ? ((byte)1) : ((byte)0));
return;
}
OpCode opcode = (UseShortForm(local) ? OpCodes.Ldloca_S : OpCodes.Ldloca);
il.Emit(opcode, local.Value);
}
else
{
LoadValue(local);
}
}
internal void Branch(CodeLabel label, bool @short)
{
OpCode opcode = (@short ? OpCodes.Br_S : OpCodes.Br);
il.Emit(opcode, label.Value);
}
internal void BranchIfFalse(CodeLabel label, bool @short)
{
OpCode opcode = (@short ? OpCodes.Brfalse_S : OpCodes.Brfalse);
il.Emit(opcode, label.Value);
}
internal void BranchIfTrue(CodeLabel label, bool @short)
{
OpCode opcode = (@short ? OpCodes.Brtrue_S : OpCodes.Brtrue);
il.Emit(opcode, label.Value);
}
internal void BranchIfEqual(CodeLabel label, bool @short)
{
OpCode opcode = (@short ? OpCodes.Beq_S : OpCodes.Beq);
il.Emit(opcode, label.Value);
}
internal void CopyValue()
{
Emit(OpCodes.Dup);
}
internal void BranchIfGreater(CodeLabel label, bool @short)
{
OpCode opcode = (@short ? OpCodes.Bgt_S : OpCodes.Bgt);
il.Emit(opcode, label.Value);
}
internal void BranchIfLess(CodeLabel label, bool @short)
{
OpCode opcode = (@short ? OpCodes.Blt_S : OpCodes.Blt);
il.Emit(opcode, label.Value);
}
internal void DiscardValue()
{
Emit(OpCodes.Pop);
}
public void Subtract()
{
Emit(OpCodes.Sub);
}
public void Switch(CodeLabel[] jumpTable)
{
if (jumpTable.Length <= 128)
{
Label[] array = new Label[jumpTable.Length];
for (int i = 0; i < array.Length; i++)
{
array[i] = jumpTable[i].Value;
}
il.Emit(OpCodes.Switch, array);
return;
}
using Local local = GetLocalWithValue(MapType(typeof(int)), null);
int num = jumpTable.Length;
int num2 = 0;
int num3 = num / 128;
if (num % 128 != 0)
{
num3++;
}
Label[] array2 = new Label[num3];
for (int j = 0; j < num3; j++)
{
array2[j] = il.DefineLabel();
}
CodeLabel label = DefineLabel();
LoadValue(local);
LoadValue(128);
Emit(OpCodes.Div);
il.Emit(OpCodes.Switch, array2);
Branch(label, @short: false);
Label[] array3 = new Label[128];
for (int k = 0; k < num3; k++)
{
il.MarkLabel(array2[k]);
int num4 = Math.Min(128, num);
num -= num4;
if (array3.Length != num4)
{
array3 = new Label[num4];
}
int num5 = num2;
for (int l = 0; l < num4; l++)
{
array3[l] = jumpTable[num2++].Value;
}
LoadValue(local);
if (num5 != 0)
{
LoadValue(num5);
Emit(OpCodes.Sub);
}
il.Emit(OpCodes.Switch, array3);
if (num != 0)
{
Branch(label, @short: false);
}
}
MarkLabel(label);
}
internal void EndFinally()
{
il.EndExceptionBlock();
}
internal void BeginFinally()
{
il.BeginFinallyBlock();
}
internal void EndTry(CodeLabel label, bool @short)
{
OpCode opcode = (@short ? OpCodes.Leave_S : OpCodes.Leave);
il.Emit(opcode, label.Value);
}
internal CodeLabel BeginTry()
{
CodeLabel result = new CodeLabel(il.BeginExceptionBlock(), nextLabel++);
return result;
}
internal void Constrain(Type type)
{
il.Emit(OpCodes.Constrained, type);
}
internal void TryCast(Type type)
{
il.Emit(OpCodes.Isinst, type);
}
internal void Cast(Type type)
{
il.Emit(OpCodes.Castclass, type);
}
public IDisposable Using(Local local)
{
return new UsingBlock(this, local);
}
internal void Add()
{
Emit(OpCodes.Add);
}
internal void LoadLength(Local arr, bool zeroIfNull)
{
if (zeroIfNull)
{
CodeLabel label = DefineLabel();
CodeLabel label2 = DefineLabel();
LoadValue(arr);
CopyValue();
BranchIfTrue(label, @short: true);
DiscardValue();
LoadValue(0);
Branch(label2, @short: true);
MarkLabel(label);
Emit(OpCodes.Ldlen);
Emit(OpCodes.Conv_I4);
MarkLabel(label2);
}
else
{
LoadValue(arr);
Emit(OpCodes.Ldlen);
Emit(OpCodes.Conv_I4);
}
}
internal void CreateArray(Type elementType, Local length)
{
LoadValue(length);
il.Emit(OpCodes.Newarr, elementType);
}
internal void LoadArrayValue(Local arr, Local i)
{
Type type = arr.Type;
type = type.GetElementType();
LoadValue(arr);
LoadValue(i);
switch (Helpers.GetTypeCode(type))
{
case ProtoTypeCode.SByte:
Emit(OpCodes.Ldelem_I1);
return;
case ProtoTypeCode.Int16:
Emit(OpCodes.Ldelem_I2);
return;
case ProtoTypeCode.Int32:
Emit(OpCodes.Ldelem_I4);
return;
case ProtoTypeCode.Int64:
Emit(OpCodes.Ldelem_I8);
return;
case ProtoTypeCode.Byte:
Emit(OpCodes.Ldelem_U1);
return;
case ProtoTypeCode.UInt16:
Emit(OpCodes.Ldelem_U2);
return;
case ProtoTypeCode.UInt32:
Emit(OpCodes.Ldelem_U4);
return;
case ProtoTypeCode.UInt64:
Emit(OpCodes.Ldelem_I8);
return;
case ProtoTypeCode.Single:
Emit(OpCodes.Ldelem_R4);
return;
case ProtoTypeCode.Double:
Emit(OpCodes.Ldelem_R8);
return;
}
if (Helpers.IsValueType(type))
{
il.Emit(OpCodes.Ldelema, type);
il.Emit(OpCodes.Ldobj, type);
}
else
{
Emit(OpCodes.Ldelem_Ref);
}
}
internal void LoadValue(Type type)
{
il.Emit(OpCodes.Ldtoken, type);
EmitCall(MapType(typeof(Type)).GetMethod("GetTypeFromHandle"));
}
internal void ConvertToInt32(ProtoTypeCode typeCode, bool uint32Overflow)
{
switch (typeCode)
{
case ProtoTypeCode.SByte:
case ProtoTypeCode.Byte:
case ProtoTypeCode.Int16:
case ProtoTypeCode.UInt16:
Emit(OpCodes.Conv_I4);
break;
case ProtoTypeCode.Int64:
Emit(OpCodes.Conv_Ovf_I4);
break;
case ProtoTypeCode.UInt32:
Emit(uint32Overflow ? OpCodes.Conv_Ovf_I4_Un : OpCodes.Conv_Ovf_I4);
break;
case ProtoTypeCode.UInt64:
Emit(OpCodes.Conv_Ovf_I4_Un);
break;
default:
throw new InvalidOperationException("ConvertToInt32 not implemented for: " + typeCode);
case ProtoTypeCode.Int32:
break;
}
}
internal void ConvertFromInt32(ProtoTypeCode typeCode, bool uint32Overflow)
{
switch (typeCode)
{
case ProtoTypeCode.SByte:
Emit(OpCodes.Conv_Ovf_I1);
break;
case ProtoTypeCode.Byte:
Emit(OpCodes.Conv_Ovf_U1);
break;
case ProtoTypeCode.Int16:
Emit(OpCodes.Conv_Ovf_I2);
break;
case ProtoTypeCode.UInt16:
Emit(OpCodes.Conv_Ovf_U2);
break;
case ProtoTypeCode.UInt32:
Emit(uint32Overflow ? OpCodes.Conv_Ovf_U4 : OpCodes.Conv_U4);
break;
case ProtoTypeCode.Int64:
Emit(OpCodes.Conv_I8);
break;
case ProtoTypeCode.UInt64:
Emit(OpCodes.Conv_U8);
break;
default:
throw new InvalidOperationException();
case ProtoTypeCode.Int32:
break;
}
}
internal void LoadValue(decimal value)
{
if (value == 0m)
{
LoadValue(typeof(decimal).GetField("Zero"));
return;
}
int[] bits = decimal.GetBits(value);
LoadValue(bits[0]);
LoadValue(bits[1]);
LoadValue(bits[2]);
LoadValue(bits[3] >>> 31);
LoadValue((bits[3] >> 16) & 0xFF);
EmitCtor(MapType(typeof(decimal)), MapType(typeof(int)), MapType(typeof(int)), MapType(typeof(int)), MapType(typeof(bool)), MapType(typeof(byte)));
}
internal void LoadValue(Guid value)
{
if (value == Guid.Empty)
{
LoadValue(typeof(Guid).GetField("Empty"));
return;
}
byte[] array = value.ToByteArray();
int value2 = array[0] | (array[1] << 8) | (array[2] << 16) | (array[3] << 24);
LoadValue(value2);
short value3 = (short)(array[4] | (array[5] << 8));
LoadValue(value3);
value3 = (short)(array[6] | (array[7] << 8));
LoadValue(value3);
for (value2 = 8; value2 <= 15; value2++)
{
LoadValue(array[value2]);
}
EmitCtor(MapType(typeof(Guid)), MapType(typeof(int)), MapType(typeof(short)), MapType(typeof(short)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)));
}
internal void LoadSerializationContext()
{
LoadReaderWriter();
LoadValue((isWriter ? typeof(ProtoWriter) : typeof(ProtoReader)).GetProperty("Context"));
}
internal Type MapType(Type type)
{
return model.MapType(type);
}
internal bool AllowInternal(PropertyInfo property)
{
if (!NonPublic)
{
return InternalsVisible(Helpers.GetAssembly(property.DeclaringType));
}
return true;
}
}