ikvm-fork/runtime/MemberWrapper.cs

1774 строки
48 KiB
C#

/*
Copyright (C) 2002, 2003, 2004, 2005, 2006 Jeroen Frijters
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jeroen Frijters
jeroen@frijters.net
*/
using System;
using System.Collections;
using System.Reflection;
#if !COMPACT_FRAMEWORK
using System.Reflection.Emit;
using ILGenerator = IKVM.Internal.CountingILGenerator;
#endif
using System.Diagnostics;
using IKVM.Attributes;
using IKVM.Runtime;
namespace IKVM.Internal
{
[Flags]
enum MemberFlags : short
{
None = 0,
HideFromReflection = 1,
ExplicitOverride = 2,
LiteralField = 4,
MirandaMethod = 8,
AccessStub = 16,
InternalAccess = 32 // member has "internal" access (@ikvm.lang.Internal)
}
class MemberWrapper
{
private System.Runtime.InteropServices.GCHandle handle;
private TypeWrapper declaringType;
private Modifiers modifiers;
private MemberFlags flags;
private string name;
private string sig;
protected MemberWrapper(TypeWrapper declaringType, string name, string sig, Modifiers modifiers, MemberFlags flags)
{
Debug.Assert(declaringType != null);
this.declaringType = declaringType;
this.name = String.Intern(name);
this.sig = String.Intern(sig);
this.modifiers = modifiers;
this.flags = flags;
}
// NOTE since we don't support unloading code, there is no need to have a finalizer
#if CLASS_GC
~MemberWrapper()
{
// NOTE when the AppDomain is being unloaded, we shouldn't clean up the handle, because
// JNI code running in a finalize can use this handle later on (since finalization is
// unordered). Note that this isn't a leak since the AppDomain is going away anyway.
if(!Environment.HasShutdownStarted && handle.IsAllocated)
{
FreeHandle();
}
}
private void FreeHandle()
{
// this has a LinkDemand, so it has to be in a separate method
handle.Free();
}
#endif
internal IntPtr Cookie
{
get
{
lock(this)
{
if(!handle.IsAllocated)
{
handle = System.Runtime.InteropServices.GCHandle.Alloc(this, System.Runtime.InteropServices.GCHandleType.Weak);
}
}
return (IntPtr)handle;
}
}
internal static MemberWrapper FromCookieImpl(IntPtr cookie)
{
return (MemberWrapper)((System.Runtime.InteropServices.GCHandle)cookie).Target;
}
internal TypeWrapper DeclaringType
{
get
{
return declaringType;
}
}
internal string Name
{
get
{
return name;
}
}
internal string Signature
{
get
{
return sig;
}
}
internal bool IsAccessibleFrom(TypeWrapper referencedType, TypeWrapper caller, TypeWrapper instance)
{
if(referencedType.IsAccessibleFrom(caller))
{
return IsPublic ||
caller == DeclaringType ||
(IsProtected && caller.IsSubTypeOf(DeclaringType) && (IsStatic || instance.IsSubTypeOf(caller))) ||
(IsInternal && caller.Assembly == DeclaringType.Assembly) ||
(!IsPrivate && caller.IsInSamePackageAs(DeclaringType));
}
return false;
}
internal bool IsHideFromReflection
{
get
{
return (flags & MemberFlags.HideFromReflection) != 0;
}
}
internal bool IsExplicitOverride
{
get
{
return (flags & MemberFlags.ExplicitOverride) != 0;
}
}
internal bool IsLiteralField
{
get
{
return (flags & MemberFlags.LiteralField) != 0;
}
set
{
flags &= ~MemberFlags.LiteralField;
flags |= value ? MemberFlags.LiteralField : MemberFlags.None;
}
}
internal bool IsMirandaMethod
{
get
{
return (flags & MemberFlags.MirandaMethod) != 0;
}
}
internal bool IsAccessStub
{
get
{
return (flags & MemberFlags.AccessStub) != 0;
}
}
internal Modifiers Modifiers
{
get
{
return modifiers;
}
}
internal bool IsStatic
{
get
{
return (modifiers & Modifiers.Static) != 0;
}
}
internal bool IsInternal
{
get
{
return (flags & MemberFlags.InternalAccess) != 0;
}
}
internal bool IsPublic
{
get
{
return (modifiers & Modifiers.Public) != 0;
}
}
internal bool IsPrivate
{
get
{
return (modifiers & Modifiers.Private) != 0;
}
}
internal bool IsProtected
{
get
{
return (modifiers & Modifiers.Protected) != 0;
}
}
internal bool IsFinal
{
get
{
return (modifiers & Modifiers.Final) != 0;
}
}
}
abstract class MethodWrapper : MemberWrapper
{
internal static readonly MethodWrapper[] EmptyArray = new MethodWrapper[0];
private MethodBase method;
private string[] declaredExceptions;
private TypeWrapper returnTypeWrapper;
private TypeWrapper[] parameterTypeWrappers;
#if !COMPACT_FRAMEWORK
internal virtual void EmitCall(ILGenerator ilgen)
{
throw new InvalidOperationException();
}
internal virtual void EmitCallvirt(ILGenerator ilgen)
{
throw new InvalidOperationException();
}
internal virtual void EmitNewobj(ILGenerator ilgen)
{
throw new InvalidOperationException();
}
#endif
internal class GhostMethodWrapper : SmartMethodWrapper
{
private MethodInfo ghostMethod;
internal GhostMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags)
: base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags)
{
// make sure we weren't handed the ghostMethod in the wrapper value type
Debug.Assert(method == null || method.DeclaringType.IsInterface);
}
private void ResolveGhostMethod()
{
if(ghostMethod == null)
{
ghostMethod = DeclaringType.TypeAsSignatureType.GetMethod(this.Name, this.GetParametersForDefineMethod());
if(ghostMethod == null)
{
throw new InvalidOperationException("Unable to resolve ghost method");
}
}
}
#if !COMPACT_FRAMEWORK
protected override void CallvirtImpl(ILGenerator ilgen)
{
ResolveGhostMethod();
ilgen.Emit(OpCodes.Call, ghostMethod);
}
#endif
#if !STATIC_COMPILER
[HideFromJava]
internal override object Invoke(object obj, object[] args, bool nonVirtual)
{
object wrapper = Activator.CreateInstance(DeclaringType.TypeAsSignatureType);
DeclaringType.GhostRefField.SetValue(wrapper, obj);
ResolveGhostMethod();
return InvokeImpl(ghostMethod, wrapper, args, nonVirtual);
}
#endif // !STATIC_COMPILER
}
internal static MethodWrapper Create(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags)
{
Debug.Assert(declaringType != null && name!= null && sig != null && method != null);
if(declaringType.IsGhost)
{
// HACK since our caller isn't aware of the ghost issues, we'll handle the method swapping
if(method.DeclaringType.IsValueType)
{
Type[] types = new Type[parameterTypes.Length];
for(int i = 0; i < types.Length; i++)
{
types[i] = parameterTypes[i].TypeAsSignatureType;
}
method = declaringType.TypeAsBaseType.GetMethod(method.Name, types);
}
return new GhostMethodWrapper(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags);
}
else if(method is ConstructorInfo)
{
return new SmartConstructorMethodWrapper(declaringType, name, sig, (ConstructorInfo)method, parameterTypes, modifiers, flags);
}
else
{
return new SmartCallMethodWrapper(declaringType, name, sig, (MethodInfo)method, returnType, parameterTypes, modifiers, flags, SimpleOpCode.Call, method.IsStatic ? SimpleOpCode.Call : SimpleOpCode.Callvirt);
}
}
internal MethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags)
: base(declaringType, name, sig, modifiers, flags)
{
Profiler.Count("MethodWrapper");
this.method = method;
Debug.Assert(((returnType == null) == (parameterTypes == null)) || (returnType == PrimitiveTypeWrapper.VOID));
this.returnTypeWrapper = returnType;
this.parameterTypeWrappers = parameterTypes;
}
internal void SetDeclaredExceptions(string[] exceptions)
{
if(exceptions == null)
{
exceptions = new string[0];
}
this.declaredExceptions = (string[])exceptions.Clone();
}
internal static MethodWrapper FromCookie(IntPtr cookie)
{
return (MethodWrapper)FromCookieImpl(cookie);
}
internal bool IsLinked
{
get
{
return method != null;
}
}
internal void Link()
{
lock(this)
{
if(parameterTypeWrappers != null)
{
return;
}
}
ClassLoaderWrapper loader = this.DeclaringType.GetClassLoader();
// TODO we need to use the actual classCache here
System.Collections.Hashtable classCache = new System.Collections.Hashtable();
TypeWrapper ret = ClassFile.RetTypeWrapperFromSig(loader, classCache, Signature);
TypeWrapper[] parameters = ClassFile.ArgTypeWrapperListFromSig(loader, classCache, Signature);
lock(this)
{
if(parameterTypeWrappers == null)
{
Debug.Assert(returnTypeWrapper == null || returnTypeWrapper == PrimitiveTypeWrapper.VOID);
returnTypeWrapper = ret;
parameterTypeWrappers = parameters;
if(method == null)
{
try
{
method = this.DeclaringType.LinkMethod(this);
}
catch
{
// HACK if linking fails, we unlink to make sure
// that the next link attempt will fail again
returnTypeWrapper = null;
parameterTypeWrappers = null;
throw;
}
}
}
}
}
[Conditional("DEBUG")]
internal void AssertLinked()
{
if(!(parameterTypeWrappers != null && returnTypeWrapper != null))
{
Tracer.Error(Tracer.Runtime, "AssertLinked failed: " + this.DeclaringType.Name + "::" + this.Name + this.Signature);
}
Debug.Assert(parameterTypeWrappers != null && returnTypeWrapper != null, this.DeclaringType.Name + "::" + this.Name + this.Signature);
}
internal TypeWrapper ReturnType
{
get
{
AssertLinked();
return returnTypeWrapper;
}
}
internal TypeWrapper[] GetParameters()
{
AssertLinked();
return parameterTypeWrappers;
}
internal Type ReturnTypeForDefineMethod
{
get
{
return ReturnType.TypeAsSignatureType;
}
}
internal Type[] GetParametersForDefineMethod()
{
TypeWrapper[] wrappers = GetParameters();
Type[] temp = new Type[wrappers.Length];
for(int i = 0; i < wrappers.Length; i++)
{
temp[i] = wrappers[i].TypeAsSignatureType;
}
return temp;
}
internal string[] GetExceptions()
{
// remapped types and dynamically compiled types have declaredExceptions set
if(declaredExceptions != null)
{
return (string[])declaredExceptions.Clone();
}
// NOTE if method is a MethodBuilder, GetCustomAttributes doesn't work (and if
// the method had any declared exceptions, the declaredExceptions field would have
// been set)
if(method != null
#if !COMPACT_FRAMEWORK
&& !(method is MethodBuilder)
#endif
)
{
ThrowsAttribute attr = AttributeHelper.GetThrows(method);
if(attr != null)
{
return attr.Classes;
}
}
return new string[0];
}
// we expose the underlying MethodBase object,
// for Java types, this is the method that contains the compiled Java bytecode
// for remapped types, this is the mbCore method (not the helper)
// Note that for some artificial methods (notably wrap() in enums), method is null
internal MethodBase GetMethod()
{
AssertLinked();
return method;
}
internal string RealName
{
get
{
AssertLinked();
return method.Name;
}
}
// this returns the Java method's attributes in .NET terms (e.g. used to create stubs for this method)
internal MethodAttributes GetMethodAttributes()
{
MethodAttributes attribs = MethodAttributes.HideBySig;
if(IsStatic)
{
attribs |= MethodAttributes.Static;
}
if(IsPublic)
{
attribs |= MethodAttributes.Public;
}
else if(IsPrivate)
{
attribs |= MethodAttributes.Private;
}
else if(IsProtected)
{
attribs |= MethodAttributes.FamORAssem;
}
else
{
attribs |= MethodAttributes.Family;
}
// constructors aren't virtual
if(!IsStatic && !IsPrivate && Name != "<init>")
{
attribs |= MethodAttributes.Virtual;
}
if(IsFinal)
{
attribs |= MethodAttributes.Final;
}
if(IsAbstract)
{
attribs |= MethodAttributes.Abstract;
}
return attribs;
}
internal bool IsAbstract
{
get
{
return (Modifiers & Modifiers.Abstract) != 0;
}
}
#if !STATIC_COMPILER
[HideFromJava]
internal virtual object Invoke(object obj, object[] args, bool nonVirtual)
{
AssertLinked();
#if !COMPACT_FRAMEWORK
// if we've still got the builder object, we need to replace it with the real thing before we can call it
if(method is MethodBuilder)
{
bool found = false;
int token = ((MethodBuilder)method).GetToken().Token;
ModuleBuilder module = (ModuleBuilder)((MethodBuilder)method).GetModule();
foreach(MethodInfo mi in this.DeclaringType.TypeAsTBD.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
{
if(module.GetMethodToken(mi).Token == token)
{
found = true;
method = mi;
break;
}
}
if(!found)
{
throw new InvalidOperationException("Failed to fixate method: " + this.DeclaringType.Name + "." + this.Name + this.Signature);
}
}
if(method is ConstructorBuilder)
{
bool found = false;
int token = ((ConstructorBuilder)method).GetToken().Token;
ModuleBuilder module = (ModuleBuilder)((ConstructorBuilder)method).GetModule();
foreach(ConstructorInfo ci in this.DeclaringType.TypeAsTBD.GetConstructors(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
if(module.GetConstructorToken(ci).Token == token)
{
found = true;
method = ci;
break;
}
}
if(!found)
{
throw new InvalidOperationException("Failed to fixate constructor: " + this.DeclaringType.Name + "." + this.Name + this.Signature);
}
}
#endif // !COMPACT_FRAMEWORK
return InvokeImpl(method, obj, args, nonVirtual);
}
private delegate object Invoker(IntPtr pFunc, object obj, object[] args);
[HideFromJava]
internal object InvokeImpl(MethodBase method, object obj, object[] args, bool nonVirtual)
{
#if !COMPACT_FRAMEWORK
Debug.Assert(!(method is MethodBuilder || method is ConstructorBuilder));
#endif // !COMPACT_FRAMEWORK
if(IsStatic)
{
// Java allows bogus 'obj' to be specified for static methods
obj = null;
}
else
{
if(ReferenceEquals(Name, "<init>"))
{
if(method is MethodInfo)
{
Debug.Assert(method.IsStatic);
// we're dealing with a constructor on a remapped type, if obj is supplied, it means
// that we should call the constructor on an already existing instance, but that isn't
// possible with remapped types
if(obj != null)
{
// the type of this exception is a bit random (note that this can only happen through JNI reflection or
// if there is a bug in serialization [which uses the ObjectInputStream.callConstructor() in classpath.cs)
throw JavaException.IncompatibleClassChangeError("Remapped type {0} doesn't support constructor invocation on an existing instance", DeclaringType.Name);
}
}
else if(obj == null)
{
// calling <init> without an instance means that we're constructing a new instance
// NOTE this means that we cannot detect a NullPointerException when calling <init> (does JNI require this?)
try
{
InvokeArgsProcessor proc = new InvokeArgsProcessor(this, method, null, args);
object o = ((ConstructorInfo)method).Invoke(proc.GetArgs());
// since we just constructed an instance, it can't possibly be a ghost
return o;
}
catch(ArgumentException x1)
{
throw JavaException.IllegalArgumentException(x1.Message);
}
catch(TargetInvocationException x)
{
throw JavaException.InvocationTargetException(IKVM.Runtime.Util.MapException(x.InnerException));
}
}
else if(!method.DeclaringType.IsInstanceOfType(obj))
{
// we're trying to initialize an existing instance of a remapped type
if(obj is System.Exception && (args == null || args.Length == 0))
{
// HACK special case for deserialization of java.lang.Throwable subclasses
// we don't call the constructor here, it will be called by Throwable.readObject()
return null;
}
else
{
// NOTE this will also happen if you try to deserialize a .NET object
// (i.e. a type that doesn't derive from our java.lang.Object).
// We might want to support that in the future (it's fairly easy, because
// the call to Object.<init> can just be ignored)
throw new NotSupportedException("Unable to partially construct object of type " + obj.GetType().FullName + " to type " + method.DeclaringType.FullName);
}
}
}
else if(nonVirtual &&
method.IsVirtual && // if the method isn't virtual, normal reflection will work
!method.IsFinal && // if the method is final, normal reflection will work
!method.DeclaringType.IsSealed && // if the type is sealed, normal reflection will work
!method.IsAbstract) // if the method is abstract, it doesn't make sense, so we'll do a virtual call
// (Sun does a virtual call for interface methods and crashes for abstract methods)
{
#if COMPACT_FRAMEWORK
throw new NotSupportedException("Reflective non-virtual method invocation is not supported on the Compact Framework");
#else
Invoker invoker = NonvirtualInvokeHelper.GetInvoker(this);
try
{
InvokeArgsProcessor proc = new InvokeArgsProcessor(this, method, obj, args);
return invoker(method.MethodHandle.GetFunctionPointer(), proc.GetObj(), proc.GetArgs());
}
catch(ArgumentException x1)
{
throw JavaException.IllegalArgumentException(x1.Message);
}
catch(TargetInvocationException x)
{
throw JavaException.InvocationTargetException(IKVM.Runtime.Util.MapException(x.InnerException));
}
#endif
}
}
try
{
InvokeArgsProcessor proc = new InvokeArgsProcessor(this, method, obj, args);
object o = method.Invoke(proc.GetObj(), proc.GetArgs());
TypeWrapper retType = this.ReturnType;
if(!retType.IsUnloadable && retType.IsGhost)
{
o = retType.GhostRefField.GetValue(o);
}
return o;
}
catch(ArgumentException x1)
{
throw JavaException.IllegalArgumentException(x1.Message);
}
catch(TargetInvocationException x)
{
throw JavaException.InvocationTargetException(IKVM.Runtime.Util.MapException(x.InnerException));
}
}
#if !COMPACT_FRAMEWORK
private class NonvirtualInvokeHelper
{
private static Hashtable cache;
private static ModuleBuilder module;
private class KeyGen : IHashCodeProvider, IComparer
{
public int GetHashCode(object o)
{
MethodWrapper mw = (MethodWrapper)o;
return mw.Signature.GetHashCode();
}
public int Compare(object x, object y)
{
MethodWrapper mw1 = (MethodWrapper)x;
MethodWrapper mw2 = (MethodWrapper)y;
if(mw1.ReturnType == mw2.ReturnType)
{
TypeWrapper[] p1 = mw1.GetParameters();
TypeWrapper[] p2 = mw2.GetParameters();
if(p1.Length == p2.Length)
{
for(int i = 0; i < p1.Length; i++)
{
if(p1[i] != p2[i])
{
return 1;
}
}
return 0;
}
}
return 1;
}
}
static NonvirtualInvokeHelper()
{
KeyGen keygen = new KeyGen();
cache = new Hashtable(keygen, keygen);
AssemblyName name = new AssemblyName();
name.Name = "NonvirtualInvoker";
AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(name, DynamicClassLoader.IsSaveDebugImage ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run);
if(DynamicClassLoader.IsSaveDebugImage)
{
module = ab.DefineDynamicModule("NonvirtualInvoker", "NonvirtualInvoker.dll");
DynamicClassLoader.RegisterForSaveDebug(ab);
}
else
{
module = ab.DefineDynamicModule("NonvirtualInvoker");
}
}
internal static Invoker GetInvoker(MethodWrapper mw)
{
lock(cache.SyncRoot)
{
Invoker inv = (Invoker)cache[mw];
if(inv == null)
{
inv = CreateInvoker(mw);
cache[mw] = inv;
}
return inv;
}
}
private static Invoker CreateInvoker(MethodWrapper mw)
{
// TODO we need to support byref arguments...
TypeBuilder typeBuilder = module.DefineType("class" + cache.Count);
MethodBuilder methodBuilder = typeBuilder.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.Static, typeof(object), new Type[] { typeof(IntPtr), typeof(object), typeof(object[]) });
AttributeHelper.HideFromJava(methodBuilder);
ILGenerator ilgen = methodBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_1);
TypeWrapper[] paramTypes = mw.GetParameters();
for(int i = 0; i < paramTypes.Length; i++)
{
ilgen.Emit(OpCodes.Ldarg_2);
ilgen.Emit(OpCodes.Ldc_I4, i);
ilgen.Emit(OpCodes.Ldelem_Ref);
if(paramTypes[i].IsUnloadable)
{
// no need to do anything
}
else if(paramTypes[i].IsPrimitive)
{
ilgen.Emit(OpCodes.Unbox, paramTypes[i].TypeAsTBD);
ilgen.Emit(OpCodes.Ldobj, paramTypes[i].TypeAsTBD);
}
else if(paramTypes[i].IsNonPrimitiveValueType)
{
paramTypes[i].EmitUnbox(ilgen);
}
}
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.EmitCalli(OpCodes.Calli, CallingConventions.HasThis, mw.ReturnTypeForDefineMethod, mw.GetParametersForDefineMethod(), null);
if(mw.ReturnType.IsUnloadable)
{
// no need to do anything
}
else if(mw.ReturnType == PrimitiveTypeWrapper.VOID)
{
ilgen.Emit(OpCodes.Ldnull);
}
else if(mw.ReturnType.IsGhost)
{
LocalBuilder local = ilgen.DeclareLocal(mw.ReturnType.TypeAsSignatureType);
ilgen.Emit(OpCodes.Stloc, local);
ilgen.Emit(OpCodes.Ldloca, local);
ilgen.Emit(OpCodes.Ldfld, mw.ReturnType.GhostRefField);
}
else if(mw.ReturnType.IsPrimitive)
{
ilgen.Emit(OpCodes.Box, mw.ReturnType.TypeAsTBD);
}
ilgen.Emit(OpCodes.Ret);
Type type = typeBuilder.CreateType();
return (Invoker)Delegate.CreateDelegate(typeof(Invoker), type.GetMethod("Invoke"));
}
}
#endif
private struct InvokeArgsProcessor
{
private object obj;
private object[] args;
internal InvokeArgsProcessor(MethodWrapper mw, MethodBase method, object original_obj, object[] original_args)
{
TypeWrapper[] argTypes = mw.GetParameters();
if(!mw.IsStatic && method.IsStatic && mw.Name != "<init>")
{
// we've been redirected to a static method, so we have to copy the 'obj' into the args
object[] nargs = new object[original_args.Length + 1];
nargs[0] = original_obj;
original_args.CopyTo(nargs, 1);
this.obj = null;
this.args = nargs;
for(int i = 0; i < argTypes.Length; i++)
{
if(!argTypes[i].IsUnloadable && argTypes[i].IsGhost)
{
object v = Activator.CreateInstance(argTypes[i].TypeAsSignatureType);
argTypes[i].GhostRefField.SetValue(v, args[i + 1]);
args[i + 1] = v;
}
}
}
else
{
this.obj = original_obj;
this.args = original_args;
for(int i = 0; i < argTypes.Length; i++)
{
if(!argTypes[i].IsUnloadable && argTypes[i].IsGhost)
{
if(this.args == original_args)
{
this.args = (object[])args.Clone();
}
object v = Activator.CreateInstance(argTypes[i].TypeAsSignatureType);
argTypes[i].GhostRefField.SetValue(v, args[i]);
this.args[i] = v;
}
}
}
}
internal object GetObj()
{
return obj;
}
internal object[] GetArgs()
{
return args;
}
}
#endif // !STATIC_COMPILER
#if !COMPACT_FRAMEWORK
internal static OpCode SimpleOpCodeToOpCode(SimpleOpCode opc)
{
switch(opc)
{
case SimpleOpCode.Call:
return OpCodes.Call;
case SimpleOpCode.Callvirt:
return OpCodes.Callvirt;
case SimpleOpCode.Newobj:
return OpCodes.Newobj;
default:
throw new InvalidOperationException();
}
}
#endif
}
class SmartMethodWrapper : MethodWrapper
{
internal SmartMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags)
: base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags)
{
}
#if !COMPACT_FRAMEWORK
protected virtual void PreEmit(ILGenerator ilgen)
{
}
internal sealed override void EmitCall(ILGenerator ilgen)
{
AssertLinked();
PreEmit(ilgen);
CallImpl(ilgen);
}
protected virtual void CallImpl(ILGenerator ilgen)
{
throw new InvalidOperationException();
}
internal sealed override void EmitCallvirt(ILGenerator ilgen)
{
AssertLinked();
PreEmit(ilgen);
if(DeclaringType.IsNonPrimitiveValueType)
{
// callvirt isn't allowed on a value type
// TODO we need to check for a null reference
CallImpl(ilgen);
}
else
{
CallvirtImpl(ilgen);
}
}
protected virtual void CallvirtImpl(ILGenerator ilgen)
{
throw new InvalidOperationException();
}
internal sealed override void EmitNewobj(ILGenerator ilgen)
{
AssertLinked();
PreEmit(ilgen);
NewobjImpl(ilgen);
if(DeclaringType.IsNonPrimitiveValueType)
{
DeclaringType.EmitBox(ilgen);
}
}
protected virtual void NewobjImpl(ILGenerator ilgen)
{
throw new InvalidOperationException();
}
#endif
}
enum SimpleOpCode : byte
{
Call,
Callvirt,
Newobj
}
sealed class SimpleCallMethodWrapper : MethodWrapper
{
private SimpleOpCode call;
private SimpleOpCode callvirt;
internal SimpleCallMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodInfo method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags, SimpleOpCode call, SimpleOpCode callvirt)
: base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags)
{
this.call = call;
this.callvirt = callvirt;
}
#if !COMPACT_FRAMEWORK
internal override void EmitCall(ILGenerator ilgen)
{
ilgen.Emit(SimpleOpCodeToOpCode(call), (MethodInfo)GetMethod());
}
internal override void EmitCallvirt(ILGenerator ilgen)
{
ilgen.Emit(SimpleOpCodeToOpCode(callvirt), (MethodInfo)GetMethod());
}
#endif
}
sealed class SmartCallMethodWrapper : SmartMethodWrapper
{
private SimpleOpCode call;
private SimpleOpCode callvirt;
internal SmartCallMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodInfo method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags, SimpleOpCode call, SimpleOpCode callvirt)
: base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags)
{
this.call = call;
this.callvirt = callvirt;
}
#if !COMPACT_FRAMEWORK
protected override void CallImpl(ILGenerator ilgen)
{
ilgen.Emit(SimpleOpCodeToOpCode(call), (MethodInfo)GetMethod());
}
protected override void CallvirtImpl(ILGenerator ilgen)
{
ilgen.Emit(SimpleOpCodeToOpCode(callvirt), (MethodInfo)GetMethod());
}
#endif
}
sealed class SmartConstructorMethodWrapper : SmartMethodWrapper
{
internal SmartConstructorMethodWrapper(TypeWrapper declaringType, string name, string sig, ConstructorInfo method, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags)
: base(declaringType, name, sig, method, PrimitiveTypeWrapper.VOID, parameterTypes, modifiers, flags)
{
}
#if !COMPACT_FRAMEWORK
protected override void CallImpl(ILGenerator ilgen)
{
ilgen.Emit(OpCodes.Call, (ConstructorInfo)GetMethod());
}
protected override void NewobjImpl(ILGenerator ilgen)
{
ilgen.Emit(OpCodes.Newobj, (ConstructorInfo)GetMethod());
}
#endif
}
// This class tests if reflection on a constant field triggers the class constructor to run
// (it shouldn't run, but on .NET 1.0 & 1.1 it does)
sealed class ReflectionOnConstant
{
private static bool isBroken;
private static System.Collections.Hashtable warnOnce;
static ReflectionOnConstant()
{
typeof(Helper).GetField("foo", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
}
internal static bool IsBroken
{
get
{
return isBroken;
}
}
internal static void IssueWarning(FieldInfo field)
{
#if !COMPACT_FRAMEWORK
// FXBUG .NET (1.0 & 1.1)
// FieldInfo.GetValue() on a literal causes the type initializer to run and
// we don't want that.
// TODO may need to find a workaround, for now we just spit out a warning
if(ReflectionOnConstant.IsBroken && field.DeclaringType.TypeInitializer != null)
{
if(Tracer.FxBug.TraceWarning)
{
if(warnOnce == null)
{
warnOnce = new System.Collections.Hashtable();
}
if(!warnOnce.ContainsKey(field.DeclaringType.FullName))
{
warnOnce.Add(field.DeclaringType.FullName, null);
Tracer.Warning(Tracer.FxBug, "Running type initializer for {0} due to CLR bug", field.DeclaringType.FullName);
}
}
}
#endif // !COMPACT_FRAMEWORK
}
private class Helper
{
internal const int foo = 1;
static Helper()
{
isBroken = true;
}
}
}
abstract class FieldWrapper : MemberWrapper
{
internal static readonly FieldWrapper[] EmptyArray = new FieldWrapper[0];
private FieldInfo field;
private TypeWrapper fieldType;
internal FieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, Modifiers modifiers, FieldInfo field, MemberFlags flags)
: base(declaringType, name, sig, modifiers, flags)
{
Debug.Assert(name != null);
Debug.Assert(sig != null);
this.fieldType = fieldType;
this.field = field;
}
internal FieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, ExModifiers modifiers, FieldInfo field)
: this(declaringType, fieldType, name, sig, modifiers.Modifiers, field,
(modifiers.IsInternal ? MemberFlags.InternalAccess : MemberFlags.None) | (field != null && field.IsLiteral ? MemberFlags.LiteralField : MemberFlags.None))
{
}
internal FieldInfo GetField()
{
AssertLinked();
return field;
}
[Conditional("DEBUG")]
internal void AssertLinked()
{
if(fieldType == null)
{
Tracer.Error(Tracer.Runtime, "AssertLinked failed: " + this.DeclaringType.Name + "::" + this.Name + " (" + this.Signature + ")");
}
Debug.Assert(fieldType != null, this.DeclaringType.Name + "::" + this.Name + " (" + this.Signature+ ")");
}
#if !STATIC_COMPILER
// NOTE used (thru IKVM.Runtime.Util.GetFieldConstantValue) by ikvmstub to find out if the
// field is a constant (and if it is, to get its value)
internal object GetConstant()
{
AssertLinked();
// only pritimives and string can be literals in Java (because the other "primitives" (like uint),
// are treated as NonPrimitiveValueTypes)
if(field != null && (fieldType.IsPrimitive || fieldType == CoreClasses.java.lang.String.Wrapper))
{
object val = null;
if(field.IsLiteral)
{
ReflectionOnConstant.IssueWarning(field);
#if WHIDBEY
val = field.GetRawConstantValue();
#else
val = field.GetValue(null);
if(val is Enum)
{
val = DotNetTypeWrapper.EnumValueFieldWrapper.GetEnumPrimitiveValue(val);
}
#endif
}
else
{
#if WHIDBEY
// TODO
#else
// In Java, instance fields can also have a ConstantValue attribute so we emulate that
// with ConstantValueAttribute (for consumption by ikvmstub only)
object[] attrib = field.GetCustomAttributes(typeof(ConstantValueAttribute), false);
if(attrib.Length == 1)
{
val = ((ConstantValueAttribute)attrib[0]).GetConstantValue();
}
#endif
}
if(val != null && !(val is string))
{
return JVM.Library.box(val);
}
return val;
}
return null;
}
#endif // !STATIC_COMPILER
internal static FieldWrapper FromCookie(IntPtr cookie)
{
return (FieldWrapper)FromCookieImpl(cookie);
}
internal TypeWrapper FieldTypeWrapper
{
get
{
AssertLinked();
return fieldType;
}
}
#if !COMPACT_FRAMEWORK
internal void EmitGet(ILGenerator ilgen)
{
AssertLinked();
EmitGetImpl(ilgen);
}
protected abstract void EmitGetImpl(ILGenerator ilgen);
internal void EmitSet(ILGenerator ilgen)
{
AssertLinked();
EmitSetImpl(ilgen);
}
protected abstract void EmitSetImpl(ILGenerator ilgen);
#endif
internal void Link()
{
lock(this)
{
if(fieldType != null)
{
return;
}
}
// TODO we need to use the actual classCache here
System.Collections.Hashtable classCache = new System.Collections.Hashtable();
TypeWrapper fld = ClassFile.FieldTypeWrapperFromSig(this.DeclaringType.GetClassLoader(), classCache, Signature);
lock(this)
{
if(fieldType == null)
{
fieldType = fld;
try
{
field = this.DeclaringType.LinkField(this);
}
catch
{
// HACK if linking fails, we unlink to make sure
// that the next link attempt will fail again
fieldType = null;
throw;
}
}
}
}
internal bool IsVolatile
{
get
{
return (Modifiers & Modifiers.Volatile) != 0;
}
}
internal static FieldWrapper Create(TypeWrapper declaringType, TypeWrapper fieldType, FieldInfo fi, string name, string sig, ExModifiers modifiers)
{
// volatile long & double field accesses must be made atomic
if((modifiers.Modifiers & Modifiers.Volatile) != 0 && (sig == "J" || sig == "D"))
{
return new VolatileLongDoubleFieldWrapper(declaringType, fieldType, fi, name, sig, modifiers);
}
return new SimpleFieldWrapper(declaringType, fieldType, fi, name, sig, modifiers);
}
private void LookupField()
{
BindingFlags bindings = BindingFlags.Public | BindingFlags.NonPublic;
if(IsStatic)
{
bindings |= BindingFlags.Static;
}
else
{
bindings |= BindingFlags.Instance;
}
// TODO instead of looking up the field by name, we should use the Token to find it.
field = DeclaringType.TypeAsTBD.GetField(Name, bindings);
this.IsLiteralField = field.IsLiteral;
Debug.Assert(field != null);
}
#if !STATIC_COMPILER
internal virtual void SetValue(object obj, object val)
{
AssertLinked();
// TODO this is a broken implementation (for one thing, it needs to support redirection)
if(field == null || field is FieldBuilder)
{
LookupField();
}
if(fieldType.IsGhost)
{
object temp = field.GetValue(obj);
fieldType.GhostRefField.SetValue(temp, val);
val = temp;
}
try
{
field.SetValue(obj, val);
}
catch(FieldAccessException x)
{
throw JavaException.IllegalAccessException(x.Message);
}
}
#endif // !STATIC_COMPILER
internal virtual object GetValue(object obj)
{
AssertLinked();
// TODO this is a broken implementation (for one thing, it needs to support redirection)
if(field == null || field is FieldBuilder)
{
LookupField();
}
#if STATIC_COMPILER
return field.GetValue(null);
#else
// FieldInfo.IsLiteral is expensive, so we have our own flag
// TODO we might be able to ensure that we always use ConstantFieldWrapper for literal fields,
// in that case the we could simply remove the check altogether.
if(IsLiteralField)
{
// on a non-broken CLR GetValue on a literal will not trigger type initialization, but on Java it should
DeclaringType.RunClassInit();
}
object val = field.GetValue(obj);
if(fieldType.IsGhost)
{
val = fieldType.GhostRefField.GetValue(val);
}
return val;
#endif // STATIC_COMPILER
}
}
sealed class SimpleFieldWrapper : FieldWrapper
{
internal SimpleFieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, FieldInfo fi, string name, string sig, ExModifiers modifiers)
: base(declaringType, fieldType, name, sig, modifiers, fi)
{
Debug.Assert(!(fieldType == PrimitiveTypeWrapper.DOUBLE || fieldType == PrimitiveTypeWrapper.LONG) || !IsVolatile);
}
#if !COMPACT_FRAMEWORK
protected override void EmitGetImpl(ILGenerator ilgen)
{
if(!IsStatic && DeclaringType.IsNonPrimitiveValueType)
{
ilgen.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD);
}
if(IsVolatile)
{
ilgen.Emit(OpCodes.Volatile);
}
ilgen.Emit(IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, GetField());
}
protected override void EmitSetImpl(ILGenerator ilgen)
{
FieldInfo fi = GetField();
if(!IsStatic && DeclaringType.IsNonPrimitiveValueType)
{
LocalBuilder temp = ilgen.DeclareLocal(FieldTypeWrapper.TypeAsSignatureType);
ilgen.Emit(OpCodes.Stloc, temp);
ilgen.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD);
ilgen.Emit(OpCodes.Ldloc, temp);
}
if(IsVolatile)
{
ilgen.Emit(OpCodes.Volatile);
}
ilgen.Emit(IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fi);
}
#endif
}
sealed class VolatileLongDoubleFieldWrapper : FieldWrapper
{
internal VolatileLongDoubleFieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, FieldInfo fi, string name, string sig, ExModifiers modifiers)
: base(declaringType, fieldType, name, sig, modifiers, fi)
{
Debug.Assert(IsVolatile);
Debug.Assert(sig == "J" || sig == "D");
}
#if !COMPACT_FRAMEWORK
protected override void EmitGetImpl(ILGenerator ilgen)
{
FieldInfo fi = GetField();
if(fi.IsStatic)
{
ilgen.Emit(OpCodes.Ldsflda, fi);
}
else
{
if(DeclaringType.IsNonPrimitiveValueType)
{
ilgen.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD);
}
ilgen.Emit(OpCodes.Ldflda, fi);
}
if(FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE)
{
ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.volatileReadDouble);
}
else
{
Debug.Assert(FieldTypeWrapper == PrimitiveTypeWrapper.LONG);
ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.volatileReadLong);
}
}
protected override void EmitSetImpl(ILGenerator ilgen)
{
FieldInfo fi = GetField();
LocalBuilder temp = ilgen.DeclareLocal(FieldTypeWrapper.TypeAsSignatureType);
ilgen.Emit(OpCodes.Stloc, temp);
if(fi.IsStatic)
{
ilgen.Emit(OpCodes.Ldsflda, fi);
}
else
{
if(DeclaringType.IsNonPrimitiveValueType)
{
ilgen.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD);
}
ilgen.Emit(OpCodes.Ldflda, fi);
}
ilgen.Emit(OpCodes.Ldloc, temp);
if(FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE)
{
ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.volatileWriteDouble);
}
else
{
Debug.Assert(FieldTypeWrapper == PrimitiveTypeWrapper.LONG);
ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.volatileWriteLong);
}
}
#endif
}
sealed class GetterFieldWrapper : FieldWrapper
{
private MethodInfo getter;
// NOTE fi may be null!
internal GetterFieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, FieldInfo fi, string name, string sig, ExModifiers modifiers, MethodInfo getter)
: base(declaringType, fieldType, name, sig, modifiers, fi)
{
Debug.Assert(!IsVolatile);
this.getter = getter;
}
internal void SetGetter(MethodInfo getter)
{
this.getter = getter;
}
#if !COMPACT_FRAMEWORK
protected override void EmitGetImpl(ILGenerator ilgen)
{
if(!IsStatic && DeclaringType.IsNonPrimitiveValueType)
{
ilgen.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD);
ilgen.Emit(OpCodes.Call, getter);
}
else
{
// NOTE we look at the static-ness of the getter method and not our own,
// because for instance fields we can still have a static getter method
ilgen.Emit(getter.IsStatic ? OpCodes.Call : OpCodes.Callvirt, getter);
}
}
protected override void EmitSetImpl(ILGenerator ilgen)
{
FieldInfo fi = GetField();
if(!IsStatic && DeclaringType.IsNonPrimitiveValueType)
{
LocalBuilder temp = ilgen.DeclareLocal(FieldTypeWrapper.TypeAsSignatureType);
ilgen.Emit(OpCodes.Stloc, temp);
ilgen.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD);
ilgen.Emit(OpCodes.Ldloc, temp);
}
ilgen.Emit(IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fi);
}
#endif
}
sealed class ConstantFieldWrapper : FieldWrapper
{
private object constant;
internal ConstantFieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, Modifiers modifiers, FieldInfo field, object constant, MemberFlags flags)
: base(declaringType, fieldType, name, sig, modifiers, field, flags)
{
Debug.Assert(IsStatic);
this.constant = constant;
}
#if !COMPACT_FRAMEWORK
protected override void EmitGetImpl(ILGenerator ilgen)
{
// Reading a field should trigger the cctor, but since we're inlining the value
// we have to trigger it explicitly
if(DeclaringType.IsInterface)
{
if(DeclaringType.HasStaticInitializer)
{
// NOTE since Everett doesn't support adding static methods to interfaces,
// EmitRunClassConstructor doesn't work for interface, so we do it manually.
// TODO once we're on Whidbey, this won't be necessary anymore.
ilgen.Emit(OpCodes.Ldtoken, DeclaringType.TypeAsBaseType);
ilgen.Emit(OpCodes.Call, typeof(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod("RunClassConstructor"));
}
}
else
{
DeclaringType.EmitRunClassConstructor(ilgen);
}
// NOTE even though you're not supposed to access a constant static final (the compiler is supposed
// to inline them), we have to support it (because it does happen, e.g. if the field becomes final
// after the referencing class was compiled, or when we're accessing an unsigned primitive .NET field)
object v = GetValue(null);
if(v == null)
{
ilgen.Emit(OpCodes.Ldnull);
}
else if(constant is int ||
constant is short ||
constant is ushort ||
constant is byte ||
constant is sbyte ||
constant is char ||
constant is bool)
{
ilgen.Emit(OpCodes.Ldc_I4, ((IConvertible)constant).ToInt32(null));
}
else if(constant is string)
{
ilgen.Emit(OpCodes.Ldstr, (string)constant);
}
else if(constant is float)
{
ilgen.Emit(OpCodes.Ldc_R4, (float)constant);
}
else if(constant is double)
{
ilgen.Emit(OpCodes.Ldc_R8, (double)constant);
}
else if(constant is long)
{
ilgen.Emit(OpCodes.Ldc_I8, (long)constant);
}
else if(constant is uint)
{
ilgen.Emit(OpCodes.Ldc_I4, unchecked((int)((IConvertible)constant).ToUInt32(null)));
}
else if(constant is ulong)
{
ilgen.Emit(OpCodes.Ldc_I8, unchecked((long)(ulong)constant));
}
else if(constant is Enum)
{
object val = DotNetTypeWrapper.EnumValueFieldWrapper.GetEnumPrimitiveValue(constant);
if(val is long)
{
ilgen.Emit(OpCodes.Ldc_I8, (long)constant);
}
else
{
ilgen.Emit(OpCodes.Ldc_I4, ((IConvertible)constant).ToInt32(null));
}
}
else
{
throw new InvalidOperationException(constant.GetType().FullName);
}
}
protected override void EmitSetImpl(ILGenerator ilgen)
{
// when constant static final fields are updated, the JIT normally doesn't see that (because the
// constant value is inlined), so we emulate that behavior by emitting a Pop
ilgen.Emit(OpCodes.Pop);
}
#endif
internal override object GetValue(object obj)
{
if(constant == null)
{
constant = GetField().GetValue(null);
}
return constant;
}
}
#if !COMPACT_FRAMEWORK
// This type is used during AOT compilation only!
sealed class AotAccessStubFieldWrapper : FieldWrapper
{
private FieldWrapper basefield;
private MethodBuilder getter;
private MethodBuilder setter;
internal AotAccessStubFieldWrapper(TypeWrapper wrapper, FieldWrapper basefield)
: base(wrapper, null, basefield.Name, basefield.Signature, basefield.Modifiers, null, MemberFlags.AccessStub | MemberFlags.HideFromReflection)
{
this.basefield = basefield;
}
internal void DoLink(TypeBuilder typeBuilder)
{
basefield.Link();
if(basefield.IsLiteralField)
{
FieldAttributes attribs = basefield.IsPublic ? FieldAttributes.Public : FieldAttributes.FamORAssem;
attribs |= FieldAttributes.Static | FieldAttributes.Literal;
FieldBuilder fb = typeBuilder.DefineField(Name, basefield.FieldTypeWrapper.TypeAsSignatureType, attribs);
AttributeHelper.HideFromReflection(fb);
fb.SetConstant(basefield.GetValue(null));
}
else
{
PropertyBuilder pb = typeBuilder.DefineProperty(Name, PropertyAttributes.None, basefield.FieldTypeWrapper.TypeAsSignatureType, Type.EmptyTypes);
AttributeHelper.HideFromReflection(pb);
MethodAttributes attribs = basefield.IsPublic ? MethodAttributes.Public : MethodAttributes.FamORAssem;
attribs |= MethodAttributes.HideBySig;
if(basefield.IsStatic)
{
attribs |= MethodAttributes.Static;
}
getter = typeBuilder.DefineMethod("get_" + Name, attribs, basefield.FieldTypeWrapper.TypeAsSignatureType, Type.EmptyTypes);
AttributeHelper.HideFromJava(getter);
pb.SetGetMethod(getter);
ILGenerator ilgen = getter.GetILGenerator();
if(!basefield.IsStatic)
{
ilgen.Emit(OpCodes.Ldarg_0);
}
basefield.EmitGet(ilgen);
ilgen.Emit(OpCodes.Ret);
if(!basefield.IsFinal)
{
setter = typeBuilder.DefineMethod("set_" + Name, attribs, null, new Type[] { basefield.FieldTypeWrapper.TypeAsSignatureType });
AttributeHelper.HideFromJava(setter);
pb.SetSetMethod(setter);
ilgen = setter.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
if(!basefield.IsStatic)
{
ilgen.Emit(OpCodes.Ldarg_1);
}
basefield.EmitSet(ilgen);
ilgen.Emit(OpCodes.Ret);
}
}
}
protected override void EmitGetImpl(CountingILGenerator ilgen)
{
if(basefield.IsLiteralField)
{
basefield.EmitGet(ilgen);
}
else
{
ilgen.Emit(OpCodes.Call, getter);
}
}
protected override void EmitSetImpl(CountingILGenerator ilgen)
{
ilgen.Emit(OpCodes.Call, setter);
}
#if !STATIC_COMPILER
internal override object GetValue(object obj)
{
// We're MemberFlags.HideFromReflection, so we should never be called
throw new InvalidOperationException();
}
internal override void SetValue(object obj, object val)
{
// We're MemberFlags.HideFromReflection, so we should never be called
throw new InvalidOperationException();
}
#endif // !STATIC_COMPILER
}
#endif
sealed class CompiledAccessStubFieldWrapper : FieldWrapper
{
private MethodInfo getter;
private MethodInfo setter;
private static Modifiers GetModifiers(PropertyInfo property)
{
// NOTE we only support the subset of modifiers that is expected for "access stub" properties
MethodInfo getter = property.GetGetMethod(true);
Modifiers modifiers = getter.IsPublic ? Modifiers.Public : Modifiers.Protected;
if(!property.CanWrite)
{
modifiers |= Modifiers.Final;
}
if(getter.IsStatic)
{
modifiers |= Modifiers.Static;
}
return modifiers;
}
internal CompiledAccessStubFieldWrapper(TypeWrapper wrapper, PropertyInfo property)
: base(wrapper, ClassLoaderWrapper.GetWrapperFromType(property.PropertyType), property.Name, ClassLoaderWrapper.GetWrapperFromType(property.PropertyType).SigName, GetModifiers(property), null, MemberFlags.AccessStub | MemberFlags.HideFromReflection)
{
this.getter = property.GetGetMethod(true);
this.setter = property.GetSetMethod(true);
}
#if !COMPACT_FRAMEWORK
protected override void EmitGetImpl(CountingILGenerator ilgen)
{
ilgen.Emit(OpCodes.Call, getter);
}
protected override void EmitSetImpl(CountingILGenerator ilgen)
{
ilgen.Emit(OpCodes.Call, setter);
}
#endif
#if !STATIC_COMPILER
internal override object GetValue(object obj)
{
// We're MemberFlags.HideFromReflection, so we should never be called
throw new InvalidOperationException();
}
internal override void SetValue(object obj, object val)
{
// We're MemberFlags.HideFromReflection, so we should never be called
throw new InvalidOperationException();
}
#endif // !STATIC_COMPILER
}
}