ikvm-fork/runtime/MemberWrapper.cs

1034 строки
28 KiB
C#
Исходник Обычный вид История

2003-05-30 16:08:59 +04:00
/*
Copyright (C) 2002 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.Reflection;
using System.Reflection.Emit;
using System.Diagnostics;
class MemberWrapper
{
private System.Runtime.InteropServices.GCHandle handle;
private TypeWrapper declaringType;
private Modifiers modifiers;
private bool hideFromReflection;
protected MemberWrapper(TypeWrapper declaringType, Modifiers modifiers, bool hideFromReflection)
{
2003-07-07 18:18:27 +04:00
Debug.Assert(declaringType != null);
2003-05-30 16:08:59 +04:00
this.declaringType = declaringType;
this.modifiers = modifiers;
this.hideFromReflection = hideFromReflection;
}
~MemberWrapper()
{
if(handle.IsAllocated)
{
handle.Free();
}
}
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 bool IsHideFromReflection
{
get
{
return hideFromReflection;
}
}
internal Modifiers Modifiers
{
get
{
return modifiers;
}
}
internal bool IsStatic
{
get
{
return (modifiers & Modifiers.Static) != 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;
}
}
}
2003-08-26 15:24:17 +04:00
class MethodWrapper : MemberWrapper
2003-05-30 16:08:59 +04:00
{
private MethodDescriptor md;
2004-06-14 14:36:38 +04:00
private MethodBase method;
2003-11-17 15:01:50 +03:00
private string[] declaredExceptions;
2003-05-30 16:08:59 +04:00
internal CodeEmitter EmitCall;
internal CodeEmitter EmitCallvirt;
internal CodeEmitter EmitNewobj;
2003-12-20 01:19:18 +03:00
private class GhostUnwrapper : CodeEmitter
{
private TypeWrapper type;
internal GhostUnwrapper(TypeWrapper type)
{
this.type = type;
}
internal override void Emit(ILGenerator ilgen)
{
LocalBuilder local = ilgen.DeclareLocal(type.TypeAsParameterType);
ilgen.Emit(OpCodes.Stloc, local);
ilgen.Emit(OpCodes.Ldloca, local);
ilgen.Emit(OpCodes.Ldfld, type.GhostRefField);
}
}
2004-06-14 14:36:38 +04:00
internal static MethodWrapper Create(TypeWrapper declaringType, MethodDescriptor md, MethodBase method, Modifiers modifiers, bool hideFromReflection)
2003-05-30 16:08:59 +04:00
{
2004-06-14 14:36:38 +04:00
Debug.Assert(declaringType != null && md != null && method != null);
2004-01-11 16:14:42 +03:00
2004-06-14 14:36:38 +04:00
MethodWrapper wrapper = new MethodWrapper(declaringType, md, method, modifiers, hideFromReflection);
2003-07-31 16:49:29 +04:00
if(declaringType.IsGhost)
{
2004-06-14 14:36:38 +04:00
wrapper.EmitCall = CodeEmitter.InternalError;
wrapper.EmitCallvirt = new GhostCallEmitter(declaringType, md, method);
wrapper.EmitNewobj = CodeEmitter.InternalError;
2003-07-31 16:49:29 +04:00
}
else
{
2004-06-14 14:36:38 +04:00
if(method is ConstructorInfo)
{
wrapper.EmitCall = CodeEmitter.Create(OpCodes.Call, (ConstructorInfo)method);
wrapper.EmitCallvirt = CodeEmitter.InternalError;
wrapper.EmitNewobj = CodeEmitter.Create(OpCodes.Newobj, (ConstructorInfo)method);
}
else
{
if(md.Name == "<init>")
{
// we're a redirected constructor (which means that the class is final),
// so EmitCall isn't available. This also means that we won't be able to invoke
// the constructor using reflection (on an existing instance).
wrapper.EmitCall = CodeEmitter.InternalError;
wrapper.EmitCallvirt = CodeEmitter.InternalError;
wrapper.EmitNewobj = CodeEmitter.Create(OpCodes.Call, (MethodInfo)method);
}
else
{
wrapper.EmitCall = CodeEmitter.Create(OpCodes.Call, (MethodInfo)method);
if(method.IsStatic)
{
// because of redirection, it can be legal to call a static method with invokevirtual
if(!wrapper.IsStatic)
{
// we don't do a null pointer check on "this", the callee is responsible for that
wrapper.EmitCallvirt = CodeEmitter.Create(OpCodes.Call, (MethodInfo)method);
}
else
{
wrapper.EmitCallvirt = CodeEmitter.InternalError;
}
}
else
{
wrapper.EmitCallvirt = CodeEmitter.Create(OpCodes.Callvirt, (MethodInfo)method);
}
wrapper.EmitNewobj = CodeEmitter.InternalError;
}
}
2003-07-31 16:49:29 +04:00
}
2003-05-30 16:08:59 +04:00
TypeWrapper retType = md.RetTypeWrapper;
2003-12-20 01:19:18 +03:00
if(!retType.IsUnloadable)
2003-05-30 16:08:59 +04:00
{
2003-12-20 01:19:18 +03:00
if(retType.IsNonPrimitiveValueType)
{
wrapper.EmitCall += CodeEmitter.CreateEmitBoxCall(retType);
wrapper.EmitCallvirt += CodeEmitter.CreateEmitBoxCall(retType);
}
else if(retType.IsGhost)
{
wrapper.EmitCall += new GhostUnwrapper(retType);
wrapper.EmitCallvirt += new GhostUnwrapper(retType);
}
2003-05-30 16:08:59 +04:00
}
if(declaringType.IsNonPrimitiveValueType)
{
if(method is ConstructorInfo)
{
2003-11-17 15:01:50 +03:00
// HACK after constructing a new object, we don't want the custom boxing rule to run
// (because that would turn "new IntPtr" into a null reference)
2004-01-11 16:14:42 +03:00
wrapper.EmitNewobj += CodeEmitter.Create(OpCodes.Box, declaringType.TypeAsTBD);
2003-05-30 16:08:59 +04:00
}
else
{
// callvirt isn't allowed on a value type
wrapper.EmitCallvirt = wrapper.EmitCall;
}
}
return wrapper;
}
2003-07-31 16:49:29 +04:00
internal class GhostCallEmitter : CodeEmitter
{
2003-08-07 18:55:43 +04:00
private MethodDescriptor md;
2003-10-17 12:08:31 +04:00
private TypeWrapper type;
private MethodInfo method;
2003-07-31 16:49:29 +04:00
2003-10-17 12:08:31 +04:00
internal GhostCallEmitter(TypeWrapper type, MethodDescriptor md, MethodBase __method)
2003-07-31 16:49:29 +04:00
{
2003-10-17 12:08:31 +04:00
this.type = type;
2003-08-07 18:55:43 +04:00
this.md = md;
2003-07-31 16:49:29 +04:00
}
internal override void Emit(ILGenerator ilgen)
{
2003-10-17 12:08:31 +04:00
if(method == null)
2003-07-31 16:49:29 +04:00
{
2003-10-17 12:08:31 +04:00
method = type.TypeAsParameterType.GetMethod(md.Name, md.ArgTypesForDefineMethod);
2003-07-31 16:49:29 +04:00
}
2003-10-17 12:08:31 +04:00
ilgen.Emit(OpCodes.Call, method);
2003-07-31 16:49:29 +04:00
}
}
2004-06-14 14:36:38 +04:00
internal MethodWrapper(TypeWrapper declaringType, MethodDescriptor md, MethodBase method, Modifiers modifiers, bool hideFromReflection)
2003-05-30 16:08:59 +04:00
: base(declaringType, modifiers, hideFromReflection)
{
Profiler.Count("MethodWrapper");
this.md = md;
2004-06-14 14:36:38 +04:00
this.method = method;
2003-05-30 16:08:59 +04:00
}
2003-11-17 15:01:50 +03:00
internal void SetDeclaredExceptions(string[] exceptions)
{
if(exceptions == null)
{
exceptions = new string[0];
}
this.declaredExceptions = (string[])exceptions.Clone();
}
internal void SetDeclaredExceptions(MapXml.Throws[] throws)
{
if(throws != null)
{
declaredExceptions = new string[throws.Length];
for(int i = 0; i < throws.Length; i++)
{
declaredExceptions[i] = throws[i].Class;
}
}
}
2003-05-30 16:08:59 +04:00
internal static MethodWrapper FromCookie(IntPtr cookie)
{
return (MethodWrapper)FromCookieImpl(cookie);
}
internal MethodDescriptor Descriptor
{
get
{
return md;
}
}
internal string Name
{
get
{
return md.Name;
}
}
2003-06-10 17:28:47 +04:00
internal bool HasUnloadableArgsOrRet
{
get
{
if(ReturnType.IsUnloadable)
{
return true;
}
foreach(TypeWrapper tw in GetParameters())
{
if(tw.IsUnloadable)
{
return true;
}
}
return false;
}
}
2003-05-30 16:08:59 +04:00
internal TypeWrapper ReturnType
{
get
{
return md.RetTypeWrapper;
}
}
internal TypeWrapper[] GetParameters()
{
return md.ArgTypeWrappers;
}
2003-12-24 14:51:41 +03:00
internal string[] GetExceptions()
2003-11-17 15:01:50 +03:00
{
// remapped types and dynamically compiled types have declaredExceptions set
if(declaredExceptions != null)
{
2003-12-24 14:51:41 +03:00
return (string[])declaredExceptions.Clone();
2003-11-17 15:01:50 +03:00
}
2004-06-14 14:36:38 +04:00
// 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 is MethodBuilder))
2003-11-17 15:01:50 +03:00
{
2004-06-14 14:36:38 +04:00
object[] attributes = method.GetCustomAttributes(typeof(ThrowsAttribute), false);
2004-05-14 13:31:54 +04:00
if(attributes.Length == 1)
2003-11-17 15:01:50 +03:00
{
2004-05-14 13:31:54 +04:00
return ((ThrowsAttribute)attributes[0]).Classes;
2003-11-17 15:01:50 +03:00
}
}
2003-12-24 14:51:41 +03:00
return new string[0];
2003-11-17 15:01:50 +03:00
}
2003-05-30 16:08:59 +04:00
// we expose the underlying MethodBase object,
// for Java types, this is the method that contains the compiled Java bytecode
2004-06-14 14:36:38 +04:00
// for remapped types, this is the mbCore method (not the helper)
2003-05-30 16:08:59 +04:00
internal MethodBase GetMethod()
{
2004-06-14 14:36:38 +04:00
return method;
2003-05-30 16:08:59 +04:00
}
2003-11-17 15:01:50 +03:00
internal string RealName
{
get
{
2004-06-14 14:36:38 +04:00
return method.Name;
2003-11-17 15:01:50 +03:00
}
}
2003-05-30 16:08:59 +04:00
// this returns the Java method's attributes in .NET terms (e.g. used to create stubs for this method)
internal MethodAttributes GetMethodAttributes()
{
MethodAttributes attribs = 0;
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 && md.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;
}
}
2003-08-26 15:24:17 +04:00
internal virtual object Invoke(object obj, object[] args, bool nonVirtual)
2003-05-30 16:08:59 +04:00
{
2004-06-14 14:36:38 +04:00
// 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)
2003-05-30 16:08:59 +04:00
{
2004-06-14 14:36:38 +04:00
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)
2003-05-30 16:08:59 +04:00
{
2004-06-14 14:36:38 +04:00
throw new InvalidOperationException("Failed to fixate method: " + this.DeclaringType.Name + "." + this.Name + this.Descriptor.Signature);
2003-05-30 16:08:59 +04:00
}
2004-06-14 14:36:38 +04:00
}
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))
2003-05-30 16:08:59 +04:00
{
2004-06-14 14:36:38 +04:00
if(module.GetConstructorToken(ci).Token == token)
{
found = true;
method = ci;
break;
}
2003-05-30 16:08:59 +04:00
}
2004-06-14 14:36:38 +04:00
if(!found)
2003-05-30 16:08:59 +04:00
{
2004-06-14 14:36:38 +04:00
throw new InvalidOperationException("Failed to fixate constructor: " + this.DeclaringType.Name + "." + this.Name + this.Descriptor.Signature);
2003-05-30 16:08:59 +04:00
}
}
2004-06-14 14:36:38 +04:00
return InvokeImpl(method, obj, args, nonVirtual);
}
internal object InvokeImpl(MethodBase method, object obj, object[] args, bool nonVirtual)
{
Debug.Assert(!(method is MethodBuilder || method is ConstructorBuilder));
if(IsStatic)
{
// Java allows bogus 'obj' to be specified for static methods
obj = null;
}
2003-05-30 16:08:59 +04:00
else
{
if(md.Name == "<init>")
{
2004-06-14 14:36:38 +04:00
if(method is MethodInfo)
2003-05-30 16:08:59 +04:00
{
2004-06-14 14:36:38 +04:00
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
2004-01-11 16:14:42 +03:00
if(obj != null)
2003-05-30 16:08:59 +04:00
{
2004-06-14 14:36:38 +04:00
// 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);
2003-05-30 16:08:59 +04:00
}
}
2004-06-14 14:36:38 +04:00
else if(obj == null)
2004-01-11 16:14:42 +03:00
{
2004-06-14 14:36:38 +04:00
// 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(ExceptionHelper.MapExceptionFast(x.InnerException));
}
2003-05-30 16:08:59 +04:00
}
2004-06-17 13:14:51 +04:00
else if(!method.DeclaringType.IsInstanceOfType(obj))
{
// we're trying to initialize an existing instance of a remapped type
// HACK special case for deserialization of java.lang.Throwable subclasses
if(obj is System.Exception && (args == null || args.Length == 0))
{
// TODO this isn't the right constructor. We should be calling Exception(ExceptionHelper.get_NullString()).
method = typeof(System.Exception).GetConstructor(Type.EmptyTypes);
}
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);
}
}
2003-05-30 16:08:59 +04:00
}
2004-06-14 14:36:38 +04:00
else if(nonVirtual && !method.IsStatic)
2003-05-30 16:08:59 +04:00
{
2004-06-14 14:36:38 +04:00
// TODO figure out how to implement this (one way would be to generate some code on the fly)
2003-05-30 16:08:59 +04:00
throw new NotImplementedException("non-virtual reflective method invocation not implemented");
}
2004-06-14 14:36:38 +04:00
}
try
{
InvokeArgsProcessor proc = new InvokeArgsProcessor(this, method, obj, args);
object o = method.Invoke(proc.GetObj(), proc.GetArgs());
TypeWrapper retType = md.RetTypeWrapper;
if(!retType.IsUnloadable && retType.IsGhost)
2003-05-30 16:08:59 +04:00
{
2004-06-14 14:36:38 +04:00
o = md.RetTypeWrapper.GhostRefField.GetValue(o);
}
return o;
}
catch(ArgumentException x1)
{
throw JavaException.IllegalArgumentException(x1.Message);
}
catch(TargetInvocationException x)
{
throw JavaException.InvocationTargetException(ExceptionHelper.MapExceptionFast(x.InnerException));
}
}
private struct InvokeArgsProcessor
{
private object obj;
private object[] args;
internal InvokeArgsProcessor(MethodWrapper mw, MethodBase method, object original_obj, object[] original_args)
{
TypeWrapper[] argTypes = mw.md.ArgTypeWrappers;
if(!mw.IsStatic && mw.DeclaringType.IsGhost)
{
object o = Activator.CreateInstance(mw.DeclaringType.TypeAsParameterType);
mw.DeclaringType.GhostRefField.SetValue(o, original_obj);
original_obj = o;
}
if(!mw.IsStatic && method.IsStatic && mw.md.Name != "<init>")
{
// we've been redirected to a static method, so we have to copy the 'obj' into the args
args = new object[original_args.Length + 1];
args[0] = original_obj;
original_args.CopyTo(args, 1);
this.obj = null;
this.args = args;
for(int i = 0; i < argTypes.Length; i++)
2003-05-30 16:08:59 +04:00
{
2004-06-14 14:36:38 +04:00
if(!argTypes[i].IsUnloadable && argTypes[i].IsGhost)
{
object v = Activator.CreateInstance(argTypes[i].TypeAsParameterType);
argTypes[i].GhostRefField.SetValue(v, args[i + 1]);
args[i + 1] = v;
}
2003-05-30 16:08:59 +04:00
}
}
else
{
2004-06-14 14:36:38 +04:00
this.obj = original_obj;
this.args = original_args;
for(int i = 0; i < argTypes.Length; i++)
2003-05-30 16:08:59 +04:00
{
2004-06-14 14:36:38 +04:00
if(!argTypes[i].IsUnloadable && argTypes[i].IsGhost)
{
if(this.args == original_args)
{
this.args = (object[])args.Clone();
}
object v = Activator.CreateInstance(argTypes[i].TypeAsParameterType);
argTypes[i].GhostRefField.SetValue(v, args[i]);
this.args[i] = v;
}
2003-05-30 16:08:59 +04:00
}
}
2004-06-14 14:36:38 +04:00
}
internal object GetObj()
{
return obj;
}
internal object[] GetArgs()
{
return args;
2003-05-30 16:08:59 +04:00
}
}
}
2003-12-20 01:19:18 +03:00
// 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)
class ReflectionOnConstant
{
private static bool isBroken;
2003-12-24 14:51:41 +03:00
private static System.Collections.Hashtable warnOnce;
2003-12-20 01:19:18 +03:00
static ReflectionOnConstant()
{
typeof(Helper).GetField("foo", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
}
internal static bool IsBroken
{
get
{
return isBroken;
}
}
2003-12-24 14:51:41 +03:00
internal static void IssueWarning(FieldInfo field)
{
2004-03-08 18:18:47 +03:00
// FXBUG .NET (1.0 & 1.1)
// FieldInfo.GetValue() on a literal causes the type initializer to run and
2003-12-24 14:51:41 +03:00
// 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)
{
2004-03-08 18:18:47 +03:00
if(Tracer.FxBug.TraceWarning)
2003-12-24 14:51:41 +03:00
{
if(warnOnce == null)
{
warnOnce = new System.Collections.Hashtable();
}
if(!warnOnce.ContainsKey(field.DeclaringType.FullName))
{
warnOnce.Add(field.DeclaringType.FullName, null);
2004-03-08 18:18:47 +03:00
Tracer.Warning(Tracer.FxBug, "Running type initializer for {0} due to CLR bug", field.DeclaringType.FullName);
2003-12-24 14:51:41 +03:00
}
}
}
}
2003-12-20 01:19:18 +03:00
private class Helper
{
internal const int foo = 1;
static Helper()
{
isBroken = true;
}
}
}
2003-11-17 15:01:50 +03:00
class FieldWrapper : MemberWrapper
2003-05-30 16:08:59 +04:00
{
private string name;
private string sig;
2003-07-07 18:18:27 +04:00
internal readonly CodeEmitter EmitGet;
internal readonly CodeEmitter EmitSet;
2003-05-30 16:08:59 +04:00
private FieldInfo field;
2003-06-10 17:28:47 +04:00
private TypeWrapper fieldType;
2003-05-30 16:08:59 +04:00
2003-08-21 14:06:34 +04:00
private FieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, Modifiers modifiers, FieldInfo field, CodeEmitter emitGet, CodeEmitter emitSet)
2003-05-30 16:08:59 +04:00
: base(declaringType, modifiers, false)
{
2003-07-07 18:18:27 +04:00
Debug.Assert(fieldType != null);
Debug.Assert(name != null);
Debug.Assert(sig != null);
Debug.Assert(emitGet != null);
Debug.Assert(emitSet != null);
2003-05-30 16:08:59 +04:00
this.name = name;
this.sig = sig;
2003-06-10 17:28:47 +04:00
this.fieldType = fieldType;
2003-08-21 14:06:34 +04:00
this.field = field;
2003-07-07 18:18:27 +04:00
this.EmitGet = emitGet;
this.EmitSet = emitSet;
2003-05-30 16:08:59 +04:00
}
2003-08-21 14:06:34 +04:00
// HACK used (indirectly thru NativeCode.java.lang.Field.getConstant) by netexp to find out if the
2004-03-16 20:10:09 +03:00
// field is a constant (and if it is, to get its value)
2003-08-21 14:06:34 +04:00
internal object GetConstant()
{
// NOTE only pritimives and string can be literals in Java (because the other "primitives" (like uint),
// are treated as NonPrimitiveValueTypes)
2004-03-20 16:25:08 +03:00
if(field != null && (fieldType.IsPrimitive || fieldType == CoreClasses.java.lang.String.Wrapper) && field.IsLiteral)
2003-08-21 14:06:34 +04:00
{
2003-12-24 14:51:41 +03:00
ReflectionOnConstant.IssueWarning(field);
2003-08-21 14:06:34 +04:00
object val = field.GetValue(null);
if(val != null && !(val is string))
{
return NativeCode.java.lang.reflect.JavaWrapper.Box(val);
}
return val;
}
return null;
}
2003-05-30 16:08:59 +04:00
internal static FieldWrapper FromCookie(IntPtr cookie)
{
return (FieldWrapper)FromCookieImpl(cookie);
}
internal string Name
{
get
{
return name;
}
}
internal TypeWrapper FieldTypeWrapper
{
get
{
2003-06-10 17:28:47 +04:00
return fieldType;
2003-05-30 16:08:59 +04:00
}
}
internal bool IsVolatile
{
get
{
return (Modifiers & Modifiers.Volatile) != 0;
}
}
private class VolatileLongDoubleGetter : CodeEmitter
{
2004-03-16 20:10:09 +03:00
private static MethodInfo volatileReadDouble = typeof(System.Threading.Thread).GetMethod("VolatileRead", new Type[] { Type.GetType("System.Double&") });
private static MethodInfo volatileReadLong = typeof(System.Threading.Thread).GetMethod("VolatileRead", new Type[] { Type.GetType("System.Int64&") });
2003-05-30 16:08:59 +04:00
private FieldInfo fi;
internal VolatileLongDoubleGetter(FieldInfo fi)
{
this.fi = fi;
}
internal override void Emit(ILGenerator ilgen)
{
2004-03-16 20:10:09 +03:00
ilgen.Emit(fi.IsStatic ? OpCodes.Ldsflda : OpCodes.Ldflda, fi);
if(fi.FieldType == typeof(double))
2003-05-30 16:08:59 +04:00
{
2004-03-16 20:10:09 +03:00
ilgen.Emit(OpCodes.Call, volatileReadDouble);
2003-05-30 16:08:59 +04:00
}
else
{
2004-03-16 20:10:09 +03:00
Debug.Assert(fi.FieldType == typeof(long));
ilgen.Emit(OpCodes.Call, volatileReadLong);
2003-05-30 16:08:59 +04:00
}
}
}
private class VolatileLongDoubleSetter : CodeEmitter
{
2004-03-16 20:10:09 +03:00
private static MethodInfo volatileWriteDouble = typeof(System.Threading.Thread).GetMethod("VolatileWrite", new Type[] { Type.GetType("System.Double&"), typeof(double) });
private static MethodInfo volatileWriteLong = typeof(System.Threading.Thread).GetMethod("VolatileWrite", new Type[] { Type.GetType("System.Int64&"), typeof(long) });
2003-05-30 16:08:59 +04:00
private FieldInfo fi;
internal VolatileLongDoubleSetter(FieldInfo fi)
{
this.fi = fi;
}
internal override void Emit(ILGenerator ilgen)
{
2004-03-16 20:10:09 +03:00
LocalBuilder temp = ilgen.DeclareLocal(fi.FieldType);
ilgen.Emit(OpCodes.Stloc, temp);
ilgen.Emit(fi.IsStatic ? OpCodes.Ldsflda : OpCodes.Ldflda, fi);
ilgen.Emit(OpCodes.Ldloc, temp);
if(fi.FieldType == typeof(double))
2003-05-30 16:08:59 +04:00
{
2004-03-16 20:10:09 +03:00
ilgen.Emit(OpCodes.Call, volatileWriteDouble);
2003-05-30 16:08:59 +04:00
}
else
{
2004-03-16 20:10:09 +03:00
Debug.Assert(fi.FieldType == typeof(long));
ilgen.Emit(OpCodes.Call, volatileWriteLong);
2003-05-30 16:08:59 +04:00
}
}
}
private class ValueTypeFieldSetter : CodeEmitter
{
private Type declaringType;
private Type fieldType;
internal ValueTypeFieldSetter(Type declaringType, Type fieldType)
{
this.declaringType = declaringType;
this.fieldType = fieldType;
}
internal override void Emit(ILGenerator ilgen)
{
LocalBuilder local = ilgen.DeclareLocal(fieldType);
ilgen.Emit(OpCodes.Stloc, local);
ilgen.Emit(OpCodes.Unbox, declaringType);
ilgen.Emit(OpCodes.Ldloc, local);
}
}
2003-10-17 12:08:31 +04:00
private class GhostFieldSetter : CodeEmitter
{
private OpCode ldflda;
private FieldInfo field;
private TypeWrapper type;
internal GhostFieldSetter(FieldInfo field, TypeWrapper type, OpCode ldflda)
{
this.field = field;
this.type = type;
this.ldflda = ldflda;
}
internal override void Emit(ILGenerator ilgen)
{
LocalBuilder local = ilgen.DeclareLocal(type.TypeAsLocalOrStackType);
ilgen.Emit(OpCodes.Stloc, local);
ilgen.Emit(ldflda, field);
ilgen.Emit(OpCodes.Ldloc, local);
ilgen.Emit(OpCodes.Stfld, type.GhostRefField);
}
}
2003-08-21 14:06:34 +04:00
internal static FieldWrapper Create(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, Modifiers modifiers, FieldInfo fi, CodeEmitter getter, CodeEmitter setter)
2003-05-30 16:08:59 +04:00
{
2003-08-21 14:06:34 +04:00
return new FieldWrapper(declaringType, fieldType, name, sig, modifiers, fi, getter, setter);
2003-06-10 17:28:47 +04:00
}
2003-11-17 15:01:50 +03:00
internal static FieldWrapper Create(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, Modifiers modifiers, FieldInfo fi, CodeEmitter getter, CodeEmitter setter, object constant)
{
if(constant != null)
{
return new ConstantFieldWrapper(declaringType, fieldType, name, sig, modifiers, fi, getter, setter, constant);
}
else
{
return new FieldWrapper(declaringType, fieldType, name, sig, modifiers, fi, getter, setter);
}
}
2003-06-10 17:28:47 +04:00
internal static FieldWrapper Create(TypeWrapper declaringType, TypeWrapper fieldType, FieldInfo fi, string sig, Modifiers modifiers)
{
2003-07-07 18:18:27 +04:00
CodeEmitter emitGet = null;
CodeEmitter emitSet = null;
2003-05-30 16:08:59 +04:00
if(declaringType.IsNonPrimitiveValueType)
{
2003-11-17 15:01:50 +03:00
// NOTE all that ValueTypeFieldSetter does, is unbox the boxed value type that contains the field that we are setting
2004-01-11 16:14:42 +03:00
emitSet = new ValueTypeFieldSetter(declaringType.TypeAsTBD, fieldType.TypeAsTBD);
emitGet = CodeEmitter.Create(OpCodes.Unbox, declaringType.TypeAsTBD);
2003-05-30 16:08:59 +04:00
}
2003-07-07 18:18:27 +04:00
if(fieldType.IsUnloadable)
2003-06-10 17:28:47 +04:00
{
2003-12-24 14:51:41 +03:00
// TODO we might need to emit code to check the type dynamically
// TODO the fact that the type is unloadable now, doesn't mean it will be unloadable when a method
// that accesses this field is compiled, that means that that method (may) need to emit a cast
if((modifiers & Modifiers.Static) != 0)
{
emitGet += CodeEmitter.Create(OpCodes.Ldsfld, fi);
emitSet += CodeEmitter.Create(OpCodes.Stsfld, fi);
}
else
{
emitGet += CodeEmitter.Create(OpCodes.Ldfld, fi);
emitSet += CodeEmitter.Create(OpCodes.Stfld, fi);
}
2003-08-21 14:06:34 +04:00
return new FieldWrapper(declaringType, fieldType, fi.Name, sig, modifiers, fi, emitGet, emitSet);
2003-06-10 17:28:47 +04:00
}
2003-10-17 12:08:31 +04:00
if(fieldType.IsGhost)
{
if((modifiers & Modifiers.Static) != 0)
{
emitGet += CodeEmitter.Create(OpCodes.Ldsflda, fi) + CodeEmitter.Create(OpCodes.Ldfld, fieldType.GhostRefField);
emitSet += new GhostFieldSetter(fi, fieldType, OpCodes.Ldsflda);
}
else
{
emitGet += CodeEmitter.Create(OpCodes.Ldflda, fi) + CodeEmitter.Create(OpCodes.Ldfld, fieldType.GhostRefField);
emitSet += new GhostFieldSetter(fi, fieldType, OpCodes.Ldflda);
}
return new FieldWrapper(declaringType, fieldType, fi.Name, sig, modifiers, fi, emitGet, emitSet);
}
2003-07-07 18:18:27 +04:00
if(fieldType.IsNonPrimitiveValueType)
2003-05-30 16:08:59 +04:00
{
2003-11-17 15:01:50 +03:00
emitSet += CodeEmitter.CreateEmitUnboxCall(fieldType);
2003-05-30 16:08:59 +04:00
}
2003-07-07 18:18:27 +04:00
if((modifiers & Modifiers.Volatile) != 0)
2003-05-30 16:08:59 +04:00
{
// long & double field accesses must be made atomic
if(fi.FieldType == typeof(long) || fi.FieldType == typeof(double))
{
2003-07-07 18:18:27 +04:00
// TODO shouldn't we use += here (for volatile fields inside of value types)?
emitGet = new VolatileLongDoubleGetter(fi);
emitSet = new VolatileLongDoubleSetter(fi);
2003-08-21 14:06:34 +04:00
return new FieldWrapper(declaringType, fieldType, fi.Name, sig, modifiers, fi, emitGet, emitSet);
2003-05-30 16:08:59 +04:00
}
2003-07-07 18:18:27 +04:00
emitGet += CodeEmitter.Volatile;
emitSet += CodeEmitter.Volatile;
2003-05-30 16:08:59 +04:00
}
2003-07-07 18:18:27 +04:00
if((modifiers & Modifiers.Static) != 0)
2003-05-30 16:08:59 +04:00
{
2003-07-07 18:18:27 +04:00
emitGet += CodeEmitter.Create(OpCodes.Ldsfld, fi);
emitSet += CodeEmitter.Create(OpCodes.Stsfld, fi);
2003-05-30 16:08:59 +04:00
}
else
{
2003-07-07 18:18:27 +04:00
emitGet += CodeEmitter.Create(OpCodes.Ldfld, fi);
emitSet += CodeEmitter.Create(OpCodes.Stfld, fi);
2003-05-30 16:08:59 +04:00
}
2003-07-07 18:18:27 +04:00
if(fieldType.IsNonPrimitiveValueType)
2003-05-30 16:08:59 +04:00
{
2003-11-17 15:01:50 +03:00
emitGet += CodeEmitter.CreateEmitBoxCall(fieldType);
2003-05-30 16:08:59 +04:00
}
2003-08-21 14:06:34 +04:00
return new FieldWrapper(declaringType, fieldType, fi.Name, sig, modifiers, fi, emitGet, emitSet);
2003-05-30 16:08:59 +04:00
}
private void LookupField()
{
BindingFlags bindings = BindingFlags.Public | BindingFlags.NonPublic;
if(IsStatic)
{
bindings |= BindingFlags.Static;
}
else
{
bindings |= BindingFlags.Instance;
}
2004-06-17 13:14:51 +04:00
// TODO instead of looking up the field by name, we should use the Token to find it.
2004-01-11 16:14:42 +03:00
field = DeclaringType.TypeAsTBD.GetField(name, bindings);
2003-11-17 15:01:50 +03:00
Debug.Assert(field != null);
2003-05-30 16:08:59 +04:00
}
internal void SetValue(object obj, object val)
{
// TODO this is a broken implementation (for one thing, it needs to support redirection)
2003-08-21 14:06:34 +04:00
if(field == null || field is FieldBuilder)
2003-05-30 16:08:59 +04:00
{
LookupField();
}
2003-10-17 12:08:31 +04:00
if(fieldType.IsGhost)
{
object temp = field.GetValue(obj);
fieldType.GhostRefField.SetValue(temp, val);
val = temp;
}
2003-12-24 14:51:41 +03:00
try
{
field.SetValue(obj, val);
}
catch(FieldAccessException x)
{
2004-01-28 14:28:16 +03:00
throw JavaException.IllegalAccessException(x.Message);
2003-12-24 14:51:41 +03:00
}
2003-05-30 16:08:59 +04:00
}
2003-11-17 15:01:50 +03:00
internal virtual object GetValue(object obj)
2003-05-30 16:08:59 +04:00
{
// TODO this is a broken implementation (for one thing, it needs to support redirection)
2003-08-21 14:06:34 +04:00
if(field == null || field is FieldBuilder)
2003-05-30 16:08:59 +04:00
{
LookupField();
}
2003-12-24 14:51:41 +03:00
if(field.IsLiteral)
{
// on a non-broken CLR GetValue on a literal will not trigger type initialization, but on Java it should
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(field.DeclaringType.TypeHandle);
}
2003-10-17 12:08:31 +04:00
object val = field.GetValue(obj);
if(fieldType.IsGhost)
{
val = fieldType.GhostRefField.GetValue(val);
}
return val;
2003-05-30 16:08:59 +04:00
}
2003-11-17 15:01:50 +03:00
// NOTE this type is only used for remapped fields, dynamically compiled classes are always finished before we
// allow reflection (so we can look at the underlying field in that case)
private class ConstantFieldWrapper : FieldWrapper
{
private object constant;
internal ConstantFieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, Modifiers modifiers, FieldInfo field, CodeEmitter emitGet, CodeEmitter emitSet, object constant)
: base(declaringType, fieldType, name, sig, modifiers, field, emitGet, emitSet)
{
this.constant = constant;
}
internal override object GetValue(object obj)
{
return constant;
}
}
2003-05-30 16:08:59 +04:00
}