linux-packaging-mono/external/ikvm/runtime/ClassLoaderWrapper.cs
Xamarin Public Jenkins dd547c45d4 Imported Upstream version 4.2.1.36
Former-commit-id: fb75898888a02f4d3a74cf0a5b841681bc4c7fa8
2015-11-10 15:04:22 +00:00

1653 lines
42 KiB
C#

/*
Copyright (C) 2002-2015 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;
#if STATIC_COMPILER || STUB_GENERATOR
using IKVM.Reflection;
using IKVM.Reflection.Emit;
using Type = IKVM.Reflection.Type;
using ProtectionDomain = System.Object;
#else
using System.Reflection;
using System.Reflection.Emit;
using ProtectionDomain = java.security.ProtectionDomain;
#endif
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Runtime.CompilerServices;
using IKVM.Attributes;
namespace IKVM.Internal
{
[Flags]
enum CodeGenOptions
{
None = 0,
Debug = 1,
NoStackTraceInfo = 2,
StrictFinalFieldSemantics = 4,
NoJNI = 8,
RemoveAsserts = 16,
NoAutomagicSerialization = 32,
DisableDynamicBinding = 64,
NoRefEmitHelpers = 128,
RemoveUnusedFields = 256,
}
[Flags]
enum LoadMode
{
// These are the modes that should be used
Find = ReturnNull,
LoadOrNull = Load | ReturnNull,
LoadOrThrow = Load | ThrowClassNotFound,
Link = Load | ReturnUnloadable | SuppressExceptions,
// call into Java class loader
Load = 0x0001,
// return value
DontReturnUnloadable = 0x0002, // This is used with a bitwise OR to disable returning unloadable
ReturnUnloadable = 0x0004,
ReturnNull = 0x0004 | DontReturnUnloadable,
ThrowClassNotFound = 0x0008 | DontReturnUnloadable,
MaskReturn = ReturnUnloadable | ReturnNull | ThrowClassNotFound,
// exceptions (not ClassNotFoundException)
SuppressExceptions = 0x0010,
// warnings
WarnClassNotFound = 0x0020,
}
#if !STUB_GENERATOR
abstract class TypeWrapperFactory
{
internal abstract ModuleBuilder ModuleBuilder { get; }
internal abstract TypeWrapper DefineClassImpl(Dictionary<string, TypeWrapper> types, TypeWrapper host, ClassFile f, ClassLoaderWrapper classLoader, ProtectionDomain protectionDomain);
internal abstract bool ReserveName(string name);
internal abstract string AllocMangledName(DynamicTypeWrapper tw);
internal abstract Type DefineUnloadable(string name);
internal abstract Type DefineDelegate(int parameterCount, bool returnVoid);
internal abstract bool HasInternalAccess { get; }
#if CLASSGC
internal abstract void AddInternalsVisibleTo(Assembly friend);
#endif
}
#endif // !STUB_GENERATOR
class ClassLoaderWrapper
{
private static readonly object wrapperLock = new object();
private static readonly Dictionary<Type, TypeWrapper> globalTypeToTypeWrapper = new Dictionary<Type, TypeWrapper>();
#if STATIC_COMPILER || STUB_GENERATOR
private static ClassLoaderWrapper bootstrapClassLoader;
#else
private static AssemblyClassLoader bootstrapClassLoader;
#endif
private static List<GenericClassLoaderWrapper> genericClassLoaders;
#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR
protected java.lang.ClassLoader javaClassLoader;
#endif
#if !STUB_GENERATOR
private TypeWrapperFactory factory;
#endif // !STUB_GENERATOR
private readonly Dictionary<string, TypeWrapper> types = new Dictionary<string, TypeWrapper>();
private readonly Dictionary<string, Thread> defineClassInProgress = new Dictionary<string, Thread>();
private List<IntPtr> nativeLibraries;
private readonly CodeGenOptions codegenoptions;
#if CLASSGC
private Dictionary<Type, TypeWrapper> typeToTypeWrapper;
private static ConditionalWeakTable<Assembly, ClassLoaderWrapper> dynamicAssemblies;
#endif
private static readonly Dictionary<Type, string> remappedTypes = new Dictionary<Type, string>();
#if STATIC_COMPILER || STUB_GENERATOR
// HACK this is used by the ahead-of-time compiler to overrule the bootstrap classloader
// when we're compiling the core class libraries and by ikvmstub with the -bootstrap option
internal static void SetBootstrapClassLoader(ClassLoaderWrapper bootstrapClassLoader)
{
Debug.Assert(ClassLoaderWrapper.bootstrapClassLoader == null);
ClassLoaderWrapper.bootstrapClassLoader = bootstrapClassLoader;
}
#endif
static ClassLoaderWrapper()
{
globalTypeToTypeWrapper[PrimitiveTypeWrapper.BOOLEAN.TypeAsTBD] = PrimitiveTypeWrapper.BOOLEAN;
globalTypeToTypeWrapper[PrimitiveTypeWrapper.BYTE.TypeAsTBD] = PrimitiveTypeWrapper.BYTE;
globalTypeToTypeWrapper[PrimitiveTypeWrapper.CHAR.TypeAsTBD] = PrimitiveTypeWrapper.CHAR;
globalTypeToTypeWrapper[PrimitiveTypeWrapper.DOUBLE.TypeAsTBD] = PrimitiveTypeWrapper.DOUBLE;
globalTypeToTypeWrapper[PrimitiveTypeWrapper.FLOAT.TypeAsTBD] = PrimitiveTypeWrapper.FLOAT;
globalTypeToTypeWrapper[PrimitiveTypeWrapper.INT.TypeAsTBD] = PrimitiveTypeWrapper.INT;
globalTypeToTypeWrapper[PrimitiveTypeWrapper.LONG.TypeAsTBD] = PrimitiveTypeWrapper.LONG;
globalTypeToTypeWrapper[PrimitiveTypeWrapper.SHORT.TypeAsTBD] = PrimitiveTypeWrapper.SHORT;
globalTypeToTypeWrapper[PrimitiveTypeWrapper.VOID.TypeAsTBD] = PrimitiveTypeWrapper.VOID;
LoadRemappedTypes();
}
internal static void LoadRemappedTypes()
{
// if we're compiling the core, coreAssembly will be null
Assembly coreAssembly = JVM.CoreAssembly;
if(coreAssembly != null && remappedTypes.Count ==0)
{
RemappedClassAttribute[] remapped = AttributeHelper.GetRemappedClasses(coreAssembly);
if(remapped.Length > 0)
{
foreach(RemappedClassAttribute r in remapped)
{
remappedTypes.Add(r.RemappedType, r.Name);
}
}
else
{
#if STATIC_COMPILER
throw new FatalCompilerErrorException(Message.CoreClassesMissing);
#else
JVM.CriticalFailure("Failed to find core classes in core library", null);
#endif
}
}
}
internal ClassLoaderWrapper(CodeGenOptions codegenoptions, object javaClassLoader)
{
this.codegenoptions = codegenoptions;
#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR
this.javaClassLoader = (java.lang.ClassLoader)javaClassLoader;
#endif
}
internal static bool IsRemappedType(Type type)
{
return remappedTypes.ContainsKey(type);
}
#if STATIC_COMPILER || STUB_GENERATOR
internal void SetRemappedType(Type type, TypeWrapper tw)
{
lock(types)
{
types.Add(tw.Name, tw);
}
lock(globalTypeToTypeWrapper)
{
globalTypeToTypeWrapper.Add(type, tw);
}
remappedTypes.Add(type, tw.Name);
}
#endif
// return the TypeWrapper if it is already loaded, this exists for DynamicTypeWrapper.SetupGhosts
// and implements ClassLoader.findLoadedClass()
internal TypeWrapper FindLoadedClass(string name)
{
if (name.Length > 1 && name[0] == '[')
{
return FindOrLoadArrayClass(name, LoadMode.Find);
}
TypeWrapper tw;
lock (types)
{
types.TryGetValue(name, out tw);
}
return tw ?? FindLoadedClassLazy(name);
}
protected virtual TypeWrapper FindLoadedClassLazy(string name)
{
return null;
}
internal TypeWrapper RegisterInitiatingLoader(TypeWrapper tw)
{
Debug.Assert(tw != null);
Debug.Assert(!tw.IsUnloadable);
Debug.Assert(!tw.IsPrimitive);
try
{
// critical code in the finally block to avoid Thread.Abort interrupting the thread
}
finally
{
tw = RegisterInitiatingLoaderCritical(tw);
}
return tw;
}
private TypeWrapper RegisterInitiatingLoaderCritical(TypeWrapper tw)
{
lock(types)
{
TypeWrapper existing;
types.TryGetValue(tw.Name, out existing);
if(existing != tw)
{
if(existing != null)
{
// another thread beat us to it, discard the new TypeWrapper and
// return the previous one
return existing;
}
// NOTE if types.ContainsKey(tw.Name) is true (i.e. the value is null),
// we currently have a DefineClass in progress on another thread and we've
// beaten that thread to the punch by loading the class from a parent class
// loader instead. This is ok as DefineClass will throw a LinkageError when
// it is done.
types[tw.Name] = tw;
}
}
return tw;
}
internal bool EmitDebugInfo
{
get
{
return (codegenoptions & CodeGenOptions.Debug) != 0;
}
}
internal bool EmitStackTraceInfo
{
get
{
// NOTE we're negating the flag here!
return (codegenoptions & CodeGenOptions.NoStackTraceInfo) == 0;
}
}
internal bool StrictFinalFieldSemantics
{
get
{
return (codegenoptions & CodeGenOptions.StrictFinalFieldSemantics) != 0;
}
}
internal bool NoJNI
{
get
{
return (codegenoptions & CodeGenOptions.NoJNI) != 0;
}
}
internal bool RemoveAsserts
{
get
{
return (codegenoptions & CodeGenOptions.RemoveAsserts) != 0;
}
}
internal bool NoAutomagicSerialization
{
get
{
return (codegenoptions & CodeGenOptions.NoAutomagicSerialization) != 0;
}
}
internal bool DisableDynamicBinding
{
get
{
return (codegenoptions & CodeGenOptions.DisableDynamicBinding) != 0;
}
}
internal bool EmitNoRefEmitHelpers
{
get
{
return (codegenoptions & CodeGenOptions.NoRefEmitHelpers) != 0;
}
}
internal bool RemoveUnusedFields
{
get
{
return (codegenoptions & CodeGenOptions.RemoveUnusedFields) != 0;
}
}
internal bool WorkaroundAbstractMethodWidening
{
get
{
// pre-Roslyn C# compiler doesn't like widening access to abstract methods
return true;
}
}
internal bool WorkaroundInterfaceFields
{
get
{
// pre-Roslyn C# compiler doesn't allow access to interface fields
return true;
}
}
internal bool WorkaroundInterfacePrivateMethods
{
get
{
// pre-Roslyn C# compiler doesn't like interfaces that have non-public methods
return true;
}
}
internal bool WorkaroundInterfaceStaticMethods
{
get
{
// pre-Roslyn C# compiler doesn't allow access to interface static methods
return true;
}
}
#if !STATIC_COMPILER && !STUB_GENERATOR
internal bool RelaxedClassNameValidation
{
get
{
#if FIRST_PASS
return true;
#else
return JVM.relaxedVerification && (javaClassLoader == null || java.lang.ClassLoader.isTrustedLoader(javaClassLoader));
#endif
}
}
#endif // !STATIC_COMPILER && !STUB_GENERATOR
protected virtual void CheckProhibitedPackage(string className)
{
if (className.StartsWith("java.", StringComparison.Ordinal))
{
throw new JavaSecurityException("Prohibited package name: " + className.Substring(0, className.LastIndexOf('.')));
}
}
#if !STUB_GENERATOR
internal TypeWrapper DefineClass(ClassFile f, ProtectionDomain protectionDomain)
{
#if !STATIC_COMPILER
string dotnetAssembly = f.IKVMAssemblyAttribute;
if(dotnetAssembly != null)
{
// It's a stub class generated by ikvmstub (or generated by the runtime when getResource was
// called on a statically compiled class).
ClassLoaderWrapper loader;
try
{
loader = ClassLoaderWrapper.GetAssemblyClassLoaderByName(dotnetAssembly);
}
catch(Exception x)
{
// TODO don't catch all exceptions here
throw new NoClassDefFoundError(f.Name + " (" + x.Message + ")");
}
TypeWrapper tw = loader.LoadClassByDottedNameFast(f.Name);
if(tw == null)
{
throw new NoClassDefFoundError(f.Name + " (type not found in " + dotnetAssembly + ")");
}
return RegisterInitiatingLoader(tw);
}
#endif
CheckProhibitedPackage(f.Name);
// check if the class already exists if we're an AssemblyClassLoader
if(FindLoadedClassLazy(f.Name) != null)
{
throw new LinkageError("duplicate class definition: " + f.Name);
}
TypeWrapper def;
try
{
// critical code in the finally block to avoid Thread.Abort interrupting the thread
}
finally
{
def = DefineClassCritical(f, protectionDomain);
}
return def;
}
private TypeWrapper DefineClassCritical(ClassFile f, ProtectionDomain protectionDomain)
{
lock(types)
{
if(types.ContainsKey(f.Name))
{
throw new LinkageError("duplicate class definition: " + f.Name);
}
// mark the type as "loading in progress", so that we can detect circular dependencies.
types.Add(f.Name, null);
defineClassInProgress.Add(f.Name, Thread.CurrentThread);
}
try
{
return GetTypeWrapperFactory().DefineClassImpl(types, null, f, this, protectionDomain);
}
finally
{
lock(types)
{
if(types[f.Name] == null)
{
// if loading the class fails, we remove the indicator that we're busy loading the class,
// because otherwise we get a ClassCircularityError if we try to load the class again.
types.Remove(f.Name);
}
defineClassInProgress.Remove(f.Name);
Monitor.PulseAll(types);
}
}
}
internal TypeWrapperFactory GetTypeWrapperFactory()
{
if(factory == null)
{
lock(this)
{
try
{
// critical code in the finally block to avoid Thread.Abort interrupting the thread
}
finally
{
if(factory == null)
{
#if CLASSGC
if(dynamicAssemblies == null)
{
Interlocked.CompareExchange(ref dynamicAssemblies, new ConditionalWeakTable<Assembly, ClassLoaderWrapper>(), null);
}
typeToTypeWrapper = new Dictionary<Type, TypeWrapper>();
DynamicClassLoader instance = DynamicClassLoader.Get(this);
dynamicAssemblies.Add(instance.ModuleBuilder.Assembly.ManifestModule.Assembly, this);
this.factory = instance;
#else
factory = DynamicClassLoader.Get(this);
#endif
}
}
}
}
return factory;
}
#endif // !STUB_GENERATOR
internal TypeWrapper LoadClassByDottedName(string name)
{
return LoadClass(name, LoadMode.LoadOrThrow);
}
internal TypeWrapper LoadClassByDottedNameFast(string name)
{
return LoadClass(name, LoadMode.LoadOrNull);
}
internal TypeWrapper LoadClass(string name, LoadMode mode)
{
Profiler.Enter("LoadClass");
try
{
TypeWrapper tw = LoadRegisteredOrPendingClass(name);
if (tw != null)
{
return tw;
}
if (name.Length > 1 && name[0] == '[')
{
tw = FindOrLoadArrayClass(name, mode);
}
else
{
tw = LoadClassImpl(name, mode);
}
if (tw != null)
{
return RegisterInitiatingLoader(tw);
}
#if STATIC_COMPILER
if (!(name.Length > 1 && name[0] == '[') && ((mode & LoadMode.WarnClassNotFound) != 0) || WarningLevelHigh)
{
IssueMessage(Message.ClassNotFound, name);
}
#else
if (!(name.Length > 1 && name[0] == '['))
{
Tracer.Error(Tracer.ClassLoading, "Class not found: {0}", name);
}
#endif
switch (mode & LoadMode.MaskReturn)
{
case LoadMode.ReturnNull:
return null;
case LoadMode.ReturnUnloadable:
return new UnloadableTypeWrapper(name);
case LoadMode.ThrowClassNotFound:
throw new ClassNotFoundException(name);
default:
throw new InvalidOperationException();
}
}
finally
{
Profiler.Leave("LoadClass");
}
}
private TypeWrapper LoadRegisteredOrPendingClass(string name)
{
TypeWrapper tw;
lock (types)
{
if (types.TryGetValue(name, out tw) && tw == null)
{
Thread defineThread;
if (defineClassInProgress.TryGetValue(name, out defineThread))
{
if (Thread.CurrentThread == defineThread)
{
throw new ClassCircularityError(name);
}
// the requested class is currently being defined by another thread,
// so we have to wait on that
while (defineClassInProgress.ContainsKey(name))
{
Monitor.Wait(types);
}
// the defineClass may have failed, so we need to use TryGetValue
types.TryGetValue(name, out tw);
}
}
}
return tw;
}
private TypeWrapper FindOrLoadArrayClass(string name, LoadMode mode)
{
int dims = 1;
while(name[dims] == '[')
{
dims++;
if(dims == name.Length)
{
// malformed class name
return null;
}
}
if(name[dims] == 'L')
{
if(!name.EndsWith(";") || name.Length <= dims + 2 || name[dims + 1] == '[')
{
// malformed class name
return null;
}
string elemClass = name.Substring(dims + 1, name.Length - dims - 2);
// NOTE it's important that we're registered as the initiating loader
// for the element type here
TypeWrapper type = LoadClass(elemClass, mode | LoadMode.DontReturnUnloadable);
if(type != null)
{
type = CreateArrayType(name, type, dims);
}
return type;
}
if(name.Length != dims + 1)
{
// malformed class name
return null;
}
switch(name[dims])
{
case 'B':
return CreateArrayType(name, PrimitiveTypeWrapper.BYTE, dims);
case 'C':
return CreateArrayType(name, PrimitiveTypeWrapper.CHAR, dims);
case 'D':
return CreateArrayType(name, PrimitiveTypeWrapper.DOUBLE, dims);
case 'F':
return CreateArrayType(name, PrimitiveTypeWrapper.FLOAT, dims);
case 'I':
return CreateArrayType(name, PrimitiveTypeWrapper.INT, dims);
case 'J':
return CreateArrayType(name, PrimitiveTypeWrapper.LONG, dims);
case 'S':
return CreateArrayType(name, PrimitiveTypeWrapper.SHORT, dims);
case 'Z':
return CreateArrayType(name, PrimitiveTypeWrapper.BOOLEAN, dims);
default:
return null;
}
}
internal TypeWrapper FindOrLoadGenericClass(string name, LoadMode mode)
{
// we don't want to expose any failures to load any of the component types
mode = (mode & LoadMode.MaskReturn) | LoadMode.ReturnNull;
// we need to handle delegate methods here (for generic delegates)
// (note that other types with manufactured inner classes such as Attribute and Enum can't be generic)
if (name.EndsWith(DotNetTypeWrapper.DelegateInterfaceSuffix))
{
TypeWrapper outer = FindOrLoadGenericClass(name.Substring(0, name.Length - DotNetTypeWrapper.DelegateInterfaceSuffix.Length), mode);
if (outer != null && outer.IsFakeTypeContainer)
{
foreach (TypeWrapper tw in outer.InnerClasses)
{
if (tw.Name == name)
{
return tw;
}
}
}
}
// generic class name grammar:
//
// mangled(open_generic_type_name) "_$$$_" M(parameter_class_name) ( "_$$_" M(parameter_class_name) )* "_$$$$_"
//
// mangled() is the normal name mangling algorithm
// M() is a replacement of "__" with "$$005F$$005F" followed by a replace of "." with "__"
//
int pos = name.IndexOf("_$$$_");
if(pos <= 0 || !name.EndsWith("_$$$$_"))
{
return null;
}
TypeWrapper def = LoadClass(name.Substring(0, pos), mode);
if (def == null || !def.TypeAsTBD.IsGenericTypeDefinition)
{
return null;
}
Type type = def.TypeAsTBD;
List<string> typeParamNames = new List<string>();
pos += 5;
int start = pos;
int nest = 0;
for(;;)
{
pos = name.IndexOf("_$$", pos);
if(pos == -1)
{
return null;
}
if(name.IndexOf("_$$_", pos, 4) == pos)
{
if(nest == 0)
{
typeParamNames.Add(name.Substring(start, pos - start));
start = pos + 4;
}
pos += 4;
}
else if(name.IndexOf("_$$$_", pos, 5) == pos)
{
nest++;
pos += 5;
}
else if(name.IndexOf("_$$$$_", pos, 6) == pos)
{
if(nest == 0)
{
if(pos + 6 != name.Length)
{
return null;
}
typeParamNames.Add(name.Substring(start, pos - start));
break;
}
nest--;
pos += 6;
}
else
{
pos += 3;
}
}
Type[] typeArguments = new Type[typeParamNames.Count];
for(int i = 0; i < typeArguments.Length; i++)
{
string s = (string)typeParamNames[i];
// only do the unmangling for non-generic types (because we don't want to convert
// the double underscores in two adjacent _$$$_ or _$$$$_ markers)
if(s.IndexOf("_$$$_") == -1)
{
s = s.Replace("__", ".");
s = s.Replace("$$005F$$005F", "__");
}
int dims = 0;
while(s.Length > dims && s[dims] == 'A')
{
dims++;
}
if(s.Length == dims)
{
return null;
}
TypeWrapper tw;
switch(s[dims])
{
case 'L':
tw = LoadClass(s.Substring(dims + 1), mode);
if(tw == null)
{
return null;
}
tw.Finish();
break;
case 'Z':
tw = PrimitiveTypeWrapper.BOOLEAN;
break;
case 'B':
tw = PrimitiveTypeWrapper.BYTE;
break;
case 'S':
tw = PrimitiveTypeWrapper.SHORT;
break;
case 'C':
tw = PrimitiveTypeWrapper.CHAR;
break;
case 'I':
tw = PrimitiveTypeWrapper.INT;
break;
case 'F':
tw = PrimitiveTypeWrapper.FLOAT;
break;
case 'J':
tw = PrimitiveTypeWrapper.LONG;
break;
case 'D':
tw = PrimitiveTypeWrapper.DOUBLE;
break;
default:
return null;
}
if(dims > 0)
{
tw = tw.MakeArrayType(dims);
}
typeArguments[i] = tw.TypeAsSignatureType;
}
try
{
type = type.MakeGenericType(typeArguments);
}
catch(ArgumentException)
{
// one of the typeArguments failed to meet the constraints
return null;
}
TypeWrapper wrapper = GetWrapperFromType(type);
if(wrapper != null && wrapper.Name != name)
{
// the name specified was not in canonical form
return null;
}
return wrapper;
}
protected virtual TypeWrapper LoadClassImpl(string name, LoadMode mode)
{
TypeWrapper tw = FindOrLoadGenericClass(name, mode);
if(tw != null)
{
return tw;
}
#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR
if((mode & LoadMode.Load) == 0)
{
return null;
}
Profiler.Enter("ClassLoader.loadClass");
try
{
java.lang.Class c = GetJavaClassLoader().loadClassInternal(name);
if(c == null)
{
return null;
}
TypeWrapper type = TypeWrapper.FromClass(c);
if(type.Name != name)
{
// the class loader is trying to trick us
return null;
}
return type;
}
catch(java.lang.ClassNotFoundException x)
{
if((mode & LoadMode.MaskReturn) == LoadMode.ThrowClassNotFound)
{
throw new ClassLoadingException(ikvm.runtime.Util.mapException(x), name);
}
return null;
}
catch(java.lang.ThreadDeath)
{
throw;
}
catch(Exception x)
{
if((mode & LoadMode.SuppressExceptions) == 0)
{
throw new ClassLoadingException(ikvm.runtime.Util.mapException(x), name);
}
if(Tracer.ClassLoading.TraceError)
{
java.lang.ClassLoader cl = GetJavaClassLoader();
if(cl != null)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
string sep = "";
while(cl != null)
{
sb.Append(sep).Append(cl);
sep = " -> ";
cl = cl.getParent();
}
Tracer.Error(Tracer.ClassLoading, "ClassLoader chain: {0}", sb);
}
Exception m = ikvm.runtime.Util.mapException(x);
Tracer.Error(Tracer.ClassLoading, m.ToString() + Environment.NewLine + m.StackTrace);
}
return null;
}
finally
{
Profiler.Leave("ClassLoader.loadClass");
}
#else
return null;
#endif
}
private static TypeWrapper CreateArrayType(string name, TypeWrapper elementTypeWrapper, int dims)
{
Debug.Assert(new String('[', dims) + elementTypeWrapper.SigName == name);
Debug.Assert(!elementTypeWrapper.IsUnloadable && !elementTypeWrapper.IsVerifierType && !elementTypeWrapper.IsArray);
Debug.Assert(dims >= 1);
return elementTypeWrapper.GetClassLoader().RegisterInitiatingLoader(new ArrayTypeWrapper(elementTypeWrapper, name));
}
#if !STATIC_COMPILER && !STUB_GENERATOR
internal virtual java.lang.ClassLoader GetJavaClassLoader()
{
#if FIRST_PASS
return null;
#else
return javaClassLoader;
#endif
}
#endif
// NOTE this exposes potentially unfinished types
internal Type[] ArgTypeListFromSig(string sig)
{
if(sig[1] == ')')
{
return Type.EmptyTypes;
}
TypeWrapper[] wrappers = ArgTypeWrapperListFromSig(sig, LoadMode.LoadOrThrow);
Type[] types = new Type[wrappers.Length];
for(int i = 0; i < wrappers.Length; i++)
{
types[i] = wrappers[i].TypeAsSignatureType;
}
return types;
}
// NOTE: this will ignore anything following the sig marker (so that it can be used to decode method signatures)
private TypeWrapper SigDecoderWrapper(ref int index, string sig, LoadMode mode)
{
switch(sig[index++])
{
case 'B':
return PrimitiveTypeWrapper.BYTE;
case 'C':
return PrimitiveTypeWrapper.CHAR;
case 'D':
return PrimitiveTypeWrapper.DOUBLE;
case 'F':
return PrimitiveTypeWrapper.FLOAT;
case 'I':
return PrimitiveTypeWrapper.INT;
case 'J':
return PrimitiveTypeWrapper.LONG;
case 'L':
{
int pos = index;
index = sig.IndexOf(';', index) + 1;
return LoadClass(sig.Substring(pos, index - pos - 1), mode);
}
case 'S':
return PrimitiveTypeWrapper.SHORT;
case 'Z':
return PrimitiveTypeWrapper.BOOLEAN;
case 'V':
return PrimitiveTypeWrapper.VOID;
case '[':
{
// TODO this can be optimized
string array = "[";
while(sig[index] == '[')
{
index++;
array += "[";
}
switch(sig[index])
{
case 'L':
{
int pos = index;
index = sig.IndexOf(';', index) + 1;
return LoadClass(array + sig.Substring(pos, index - pos), mode);
}
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
return LoadClass(array + sig[index++], mode);
default:
throw new InvalidOperationException(sig.Substring(index));
}
}
default:
throw new InvalidOperationException(sig.Substring(index));
}
}
internal TypeWrapper FieldTypeWrapperFromSig(string sig, LoadMode mode)
{
int index = 0;
return SigDecoderWrapper(ref index, sig, mode);
}
internal TypeWrapper RetTypeWrapperFromSig(string sig, LoadMode mode)
{
int index = sig.IndexOf(')') + 1;
return SigDecoderWrapper(ref index, sig, mode);
}
internal TypeWrapper[] ArgTypeWrapperListFromSig(string sig, LoadMode mode)
{
if(sig[1] == ')')
{
return TypeWrapper.EmptyArray;
}
List<TypeWrapper> list = new List<TypeWrapper>();
for(int i = 1; sig[i] != ')';)
{
list.Add(SigDecoderWrapper(ref i, sig, mode));
}
return list.ToArray();
}
#if STATIC_COMPILER || STUB_GENERATOR
internal static ClassLoaderWrapper GetBootstrapClassLoader()
#else
internal static AssemblyClassLoader GetBootstrapClassLoader()
#endif
{
lock(wrapperLock)
{
if(bootstrapClassLoader == null)
{
bootstrapClassLoader = new BootstrapClassLoader();
}
return bootstrapClassLoader;
}
}
#if !STATIC_COMPILER && !STUB_GENERATOR
internal static ClassLoaderWrapper GetClassLoaderWrapper(java.lang.ClassLoader javaClassLoader)
{
if(javaClassLoader == null)
{
return GetBootstrapClassLoader();
}
lock(wrapperLock)
{
#if FIRST_PASS
ClassLoaderWrapper wrapper = null;
#else
ClassLoaderWrapper wrapper =
#if __MonoCS__
// MONOBUG the redundant cast to ClassLoaderWrapper is to workaround an mcs bug
(ClassLoaderWrapper)(object)
#endif
javaClassLoader.wrapper;
#endif
if(wrapper == null)
{
CodeGenOptions opt = CodeGenOptions.None;
if(JVM.EmitSymbols)
{
opt |= CodeGenOptions.Debug;
}
#if NET_4_0
if (!AppDomain.CurrentDomain.IsFullyTrusted)
{
opt |= CodeGenOptions.NoAutomagicSerialization;
}
#endif
wrapper = new ClassLoaderWrapper(opt, javaClassLoader);
SetWrapperForClassLoader(javaClassLoader, wrapper);
}
return wrapper;
}
}
#endif
#if CLASSGC
internal static ClassLoaderWrapper GetClassLoaderForDynamicJavaAssembly(Assembly asm)
{
ClassLoaderWrapper loader;
dynamicAssemblies.TryGetValue(asm, out loader);
return loader;
}
#endif // CLASSGC
internal static TypeWrapper GetWrapperFromType(Type type)
{
#if STATIC_COMPILER
if (type.__ContainsMissingType)
{
return new UnloadableTypeWrapper(type);
}
#endif
//Tracer.Info(Tracer.Runtime, "GetWrapperFromType: {0}", type.AssemblyQualifiedName);
#if !STATIC_COMPILER
TypeWrapper.AssertFinished(type);
#endif
Debug.Assert(!type.IsPointer);
Debug.Assert(!type.IsByRef);
TypeWrapper wrapper;
lock(globalTypeToTypeWrapper)
{
globalTypeToTypeWrapper.TryGetValue(type, out wrapper);
}
if(wrapper != null)
{
return wrapper;
}
#if STUB_GENERATOR
if(type.__IsMissing || type.__ContainsMissingType)
{
wrapper = new UnloadableTypeWrapper("Missing/" + type.Assembly.FullName);
globalTypeToTypeWrapper.Add(type, wrapper);
return wrapper;
}
#endif
string remapped;
if(remappedTypes.TryGetValue(type, out remapped))
{
wrapper = LoadClassCritical(remapped);
}
else if(ReflectUtil.IsVector(type))
{
// it might be an array of a dynamically compiled Java type
int rank = 1;
Type elem = type.GetElementType();
while(ReflectUtil.IsVector(elem))
{
rank++;
elem = elem.GetElementType();
}
wrapper = GetWrapperFromType(elem).MakeArrayType(rank);
}
else
{
Assembly asm = type.Assembly;
#if CLASSGC
ClassLoaderWrapper loader = null;
if(dynamicAssemblies != null && dynamicAssemblies.TryGetValue(asm, out loader))
{
lock(loader.typeToTypeWrapper)
{
TypeWrapper tw;
if(loader.typeToTypeWrapper.TryGetValue(type, out tw))
{
return tw;
}
// it must be an anonymous type then
Debug.Assert(AnonymousTypeWrapper.IsAnonymous(type));
}
}
#endif
#if !STATIC_COMPILER && !STUB_GENERATOR
if(AnonymousTypeWrapper.IsAnonymous(type))
{
Dictionary<Type, TypeWrapper> typeToTypeWrapper;
#if CLASSGC
typeToTypeWrapper = loader != null ? loader.typeToTypeWrapper : globalTypeToTypeWrapper;
#else
typeToTypeWrapper = globalTypeToTypeWrapper;
#endif
TypeWrapper tw = new AnonymousTypeWrapper(type);
lock(typeToTypeWrapper)
{
if(!typeToTypeWrapper.TryGetValue(type, out wrapper))
{
typeToTypeWrapper.Add(type, wrapper = tw);
}
}
return wrapper;
}
if(ReflectUtil.IsReflectionOnly(type))
{
// historically we've always returned null for types that don't have a corresponding TypeWrapper (or java.lang.Class)
return null;
}
#endif
// if the wrapper doesn't already exist, that must mean that the type
// is a .NET type (or a pre-compiled Java class), which means that it
// was "loaded" by an assembly classloader
wrapper = AssemblyClassLoader.FromAssembly(asm).GetWrapperFromAssemblyType(type);
}
#if CLASSGC
if(type.Assembly.IsDynamic)
{
// don't cache types in dynamic assemblies, because they might live in a RunAndCollect assembly
// TODO we also shouldn't cache generic type instances that have a GCable type parameter
return wrapper;
}
#endif
lock(globalTypeToTypeWrapper)
{
try
{
// critical code in the finally block to avoid Thread.Abort interrupting the thread
}
finally
{
globalTypeToTypeWrapper[type] = wrapper;
}
}
return wrapper;
}
internal static ClassLoaderWrapper GetGenericClassLoader(TypeWrapper wrapper)
{
Type type = wrapper.TypeAsTBD;
Debug.Assert(type.IsGenericType);
Debug.Assert(!type.ContainsGenericParameters);
List<ClassLoaderWrapper> list = new List<ClassLoaderWrapper>();
list.Add(AssemblyClassLoader.FromAssembly(type.Assembly));
foreach(Type arg in type.GetGenericArguments())
{
ClassLoaderWrapper loader = GetWrapperFromType(arg).GetClassLoader();
if(!list.Contains(loader) && loader != bootstrapClassLoader)
{
list.Add(loader);
}
}
ClassLoaderWrapper[] key = list.ToArray();
ClassLoaderWrapper matchingLoader = GetGenericClassLoaderByKey(key);
matchingLoader.RegisterInitiatingLoader(wrapper);
return matchingLoader;
}
#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR
internal static object DoPrivileged(java.security.PrivilegedAction action)
{
return java.security.AccessController.doPrivileged(action, ikvm.@internal.CallerID.create(typeof(java.lang.ClassLoader).TypeHandle));
}
#endif
private static ClassLoaderWrapper GetGenericClassLoaderByKey(ClassLoaderWrapper[] key)
{
lock(wrapperLock)
{
if(genericClassLoaders == null)
{
genericClassLoaders = new List<GenericClassLoaderWrapper>();
}
foreach(GenericClassLoaderWrapper loader in genericClassLoaders)
{
if(loader.Matches(key))
{
return loader;
}
}
#if STATIC_COMPILER || STUB_GENERATOR || FIRST_PASS
GenericClassLoaderWrapper newLoader = new GenericClassLoaderWrapper(key, null);
#else
java.lang.ClassLoader javaClassLoader = new ikvm.runtime.GenericClassLoader();
GenericClassLoaderWrapper newLoader = new GenericClassLoaderWrapper(key, javaClassLoader);
SetWrapperForClassLoader(javaClassLoader, newLoader);
#endif
genericClassLoaders.Add(newLoader);
return newLoader;
}
}
#if !STATIC_COMPILER && !STUB_GENERATOR
protected internal static void SetWrapperForClassLoader(java.lang.ClassLoader javaClassLoader, ClassLoaderWrapper wrapper)
{
#if __MonoCS__ || FIRST_PASS
typeof(java.lang.ClassLoader).GetField("wrapper", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(javaClassLoader, wrapper);
#else
javaClassLoader.wrapper = wrapper;
#endif
}
#endif
#if !STATIC_COMPILER && !STUB_GENERATOR
internal static ClassLoaderWrapper GetGenericClassLoaderByName(string name)
{
Debug.Assert(name.StartsWith("[[") && name.EndsWith("]]"));
Stack<List<ClassLoaderWrapper>> stack = new Stack<List<ClassLoaderWrapper>>();
List<ClassLoaderWrapper> list = null;
for(int i = 0; i < name.Length; i++)
{
if(name[i] == '[')
{
if(name[i + 1] == '[')
{
stack.Push(list);
list = new List<ClassLoaderWrapper>();
if(name[i + 2] == '[')
{
i++;
}
}
else
{
int start = i + 1;
i = name.IndexOf(']', i);
list.Add(ClassLoaderWrapper.GetAssemblyClassLoaderByName(name.Substring(start, i - start)));
}
}
else if(name[i] == ']')
{
ClassLoaderWrapper loader = GetGenericClassLoaderByKey(list.ToArray());
list = stack.Pop();
if(list == null)
{
return loader;
}
list.Add(loader);
}
else
{
throw new InvalidOperationException();
}
}
throw new InvalidOperationException();
}
internal static ClassLoaderWrapper GetAssemblyClassLoaderByName(string name)
{
if(name.StartsWith("[["))
{
return GetGenericClassLoaderByName(name);
}
return AssemblyClassLoader.FromAssembly(Assembly.Load(name));
}
#endif
internal static int GetGenericClassLoaderId(ClassLoaderWrapper wrapper)
{
lock(wrapperLock)
{
return genericClassLoaders.IndexOf(wrapper as GenericClassLoaderWrapper);
}
}
internal static ClassLoaderWrapper GetGenericClassLoaderById(int id)
{
lock(wrapperLock)
{
return genericClassLoaders[id];
}
}
internal void SetWrapperForType(Type type, TypeWrapper wrapper)
{
#if !STATIC_COMPILER
TypeWrapper.AssertFinished(type);
#endif
Dictionary<Type, TypeWrapper> dict;
#if CLASSGC
dict = typeToTypeWrapper ?? globalTypeToTypeWrapper;
#else
dict = globalTypeToTypeWrapper;
#endif
lock (dict)
{
try
{
// critical code in the finally block to avoid Thread.Abort interrupting the thread
}
finally
{
dict.Add(type, wrapper);
}
}
}
internal static TypeWrapper LoadClassCritical(string name)
{
#if STATIC_COMPILER
TypeWrapper wrapper = GetBootstrapClassLoader().LoadClassByDottedNameFast(name);
if (wrapper == null)
{
throw new FatalCompilerErrorException(Message.CriticalClassNotFound, name);
}
return wrapper;
#else
try
{
return GetBootstrapClassLoader().LoadClassByDottedName(name);
}
catch(Exception x)
{
JVM.CriticalFailure("Loading of critical class failed", x);
return null;
}
#endif
}
internal void RegisterNativeLibrary(IntPtr p)
{
lock(this)
{
try
{
// critical code in the finally block to avoid Thread.Abort interrupting the thread
}
finally
{
if(nativeLibraries == null)
{
nativeLibraries = new List<IntPtr>();
}
nativeLibraries.Add(p);
}
}
}
internal void UnregisterNativeLibrary(IntPtr p)
{
lock(this)
{
try
{
// critical code in the finally block to avoid Thread.Abort interrupting the thread
}
finally
{
nativeLibraries.Remove(p);
}
}
}
internal IntPtr[] GetNativeLibraries()
{
lock(this)
{
if(nativeLibraries == null)
{
return new IntPtr[0];
}
return nativeLibraries.ToArray();
}
}
#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR
public override string ToString()
{
object javaClassLoader = GetJavaClassLoader();
if(javaClassLoader == null)
{
return "null";
}
return String.Format("{0}@{1:X}", GetWrapperFromType(javaClassLoader.GetType()).Name, javaClassLoader.GetHashCode());
}
#endif
internal virtual bool InternalsVisibleToImpl(TypeWrapper wrapper, TypeWrapper friend)
{
Debug.Assert(wrapper.GetClassLoader() == this);
return this == friend.GetClassLoader();
}
#if !STATIC_COMPILER && !STUB_GENERATOR
// this method is used by IKVM.Runtime.JNI
internal static ClassLoaderWrapper FromCallerID(ikvm.@internal.CallerID callerID)
{
#if FIRST_PASS
return null;
#else
return GetClassLoaderWrapper(callerID.getCallerClassLoader());
#endif
}
#endif
#if STATIC_COMPILER
internal virtual void IssueMessage(Message msgId, params string[] values)
{
// it's not ideal when we end up here (because it means we're emitting a warning that is not associated with a specific output target),
// but it happens when we're decoding something in a referenced assembly that either doesn't make sense or contains an unloadable type
StaticCompiler.IssueMessage(msgId, values);
}
#endif
internal void CheckPackageAccess(TypeWrapper tw, ProtectionDomain pd)
{
#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR
if (javaClassLoader != null)
{
javaClassLoader.checkPackageAccess(tw.ClassObject, pd);
}
#endif
}
#if !STUB_GENERATOR
internal ClassFileParseOptions ClassFileParseOptions
{
get
{
#if STATIC_COMPILER
ClassFileParseOptions cfp = ClassFileParseOptions.LocalVariableTable;
if (EmitStackTraceInfo)
{
cfp |= ClassFileParseOptions.LineNumberTable;
}
if (bootstrapClassLoader is CompilerClassLoader)
{
cfp |= ClassFileParseOptions.TrustedAnnotations;
}
if (RemoveAsserts)
{
cfp |= ClassFileParseOptions.RemoveAssertions;
}
return cfp;
#else
ClassFileParseOptions cfp = ClassFileParseOptions.LineNumberTable;
if (EmitDebugInfo)
{
cfp |= ClassFileParseOptions.LocalVariableTable;
}
if (RelaxedClassNameValidation)
{
cfp |= ClassFileParseOptions.RelaxedClassNameValidation;
}
if (this == bootstrapClassLoader)
{
cfp |= ClassFileParseOptions.TrustedAnnotations;
}
return cfp;
#endif
}
}
#endif
#if STATIC_COMPILER
internal virtual bool WarningLevelHigh
{
get { return false; }
}
internal virtual bool NoParameterReflection
{
get { return false; }
}
#endif
}
sealed class GenericClassLoaderWrapper : ClassLoaderWrapper
{
private readonly ClassLoaderWrapper[] delegates;
internal GenericClassLoaderWrapper(ClassLoaderWrapper[] delegates, object javaClassLoader)
: base(CodeGenOptions.None, javaClassLoader)
{
this.delegates = delegates;
}
internal bool Matches(ClassLoaderWrapper[] key)
{
if(key.Length == delegates.Length)
{
for(int i = 0; i < key.Length; i++)
{
if(key[i] != delegates[i])
{
return false;
}
}
return true;
}
return false;
}
protected override TypeWrapper FindLoadedClassLazy(string name)
{
TypeWrapper tw1 = FindOrLoadGenericClass(name, LoadMode.Find);
if (tw1 != null)
{
return tw1;
}
foreach (ClassLoaderWrapper loader in delegates)
{
TypeWrapper tw = loader.FindLoadedClass(name);
if (tw != null && tw.GetClassLoader() == loader)
{
return tw;
}
}
return null;
}
internal string GetName()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append('[');
foreach(ClassLoaderWrapper loader in delegates)
{
sb.Append('[');
GenericClassLoaderWrapper gcl = loader as GenericClassLoaderWrapper;
if(gcl != null)
{
sb.Append(gcl.GetName());
}
else
{
sb.Append(((AssemblyClassLoader)loader).MainAssembly.FullName);
}
sb.Append(']');
}
sb.Append(']');
return sb.ToString();
}
#if !STATIC_COMPILER && !STUB_GENERATOR
internal java.util.Enumeration GetResources(string name)
{
#if FIRST_PASS
return null;
#else
java.util.Vector v = new java.util.Vector();
foreach (java.net.URL url in GetBootstrapClassLoader().GetResources(name))
{
v.add(url);
}
if (name.EndsWith(".class", StringComparison.Ordinal) && name.IndexOf('.') == name.Length - 6)
{
TypeWrapper tw = FindLoadedClass(name.Substring(0, name.Length - 6).Replace('/', '.'));
if (tw != null && !tw.IsArray && !tw.IsDynamic)
{
ClassLoaderWrapper loader = tw.GetClassLoader();
if (loader is GenericClassLoaderWrapper)
{
v.add(new java.net.URL("ikvmres", "gen", ClassLoaderWrapper.GetGenericClassLoaderId(loader), "/" + name));
}
else if (loader is AssemblyClassLoader)
{
foreach (java.net.URL url in ((AssemblyClassLoader)loader).FindResources(name))
{
v.add(url);
}
}
}
}
return v.elements();
#endif
}
internal java.net.URL FindResource(string name)
{
#if !FIRST_PASS
if (name.EndsWith(".class", StringComparison.Ordinal) && name.IndexOf('.') == name.Length - 6)
{
TypeWrapper tw = FindLoadedClass(name.Substring(0, name.Length - 6).Replace('/', '.'));
if (tw != null && tw.GetClassLoader() == this && !tw.IsArray && !tw.IsDynamic)
{
return new java.net.URL("ikvmres", "gen", ClassLoaderWrapper.GetGenericClassLoaderId(this), "/" + name);
}
}
#endif
return null;
}
#endif
}
}