diff --git a/classpath/allsources.lst b/classpath/allsources.lst index ae86250b..aca5358b 100644 --- a/classpath/allsources.lst +++ b/classpath/allsources.lst @@ -2419,6 +2419,7 @@ sun/misc/Ref.java ../../classpath/javax/swing/SizeRequirements.java ../../classpath/javax/swing/SizeSequence.java ../../classpath/javax/swing/SortingFocusTraversalPolicy.java +../../classpath/javax/swing/SpinnerDateModel.java ../../classpath/javax/swing/SpinnerListModel.java ../../classpath/javax/swing/SpinnerModel.java ../../classpath/javax/swing/SpinnerNumberModel.java @@ -2734,7 +2735,7 @@ sun/misc/Ref.java ../../classpath/gnu/xml/aelfred2/XmlReader.java ../../classpath/gnu/xml/dom/Consumer.java ../../classpath/gnu/xml/dom/DomAttr.java -../../classpath/gnu/xml/dom/DomCDATA.java +../../classpath/gnu/xml/dom/DomCDATASection.java ../../classpath/gnu/xml/dom/DomCharacterData.java ../../classpath/gnu/xml/dom/DomComment.java ../../classpath/gnu/xml/dom/DomDoctype.java @@ -2742,13 +2743,13 @@ sun/misc/Ref.java ../../classpath/gnu/xml/dom/DomDocumentBuilder.java ../../classpath/gnu/xml/dom/DomDocumentBuilderFactory.java ../../classpath/gnu/xml/dom/DomDocumentConfiguration.java +../../classpath/gnu/xml/dom/DomDocumentFragment.java +../../classpath/gnu/xml/dom/DomDOMException.java ../../classpath/gnu/xml/dom/DomElement.java ../../classpath/gnu/xml/dom/DomEntity.java ../../classpath/gnu/xml/dom/DomEntityReference.java ../../classpath/gnu/xml/dom/DomEvent.java -../../classpath/gnu/xml/dom/DomEx.java ../../classpath/gnu/xml/dom/DomExtern.java -../../classpath/gnu/xml/dom/DomFragment.java ../../classpath/gnu/xml/dom/DomImpl.java ../../classpath/gnu/xml/dom/DomIterator.java ../../classpath/gnu/xml/dom/DomNamedNodeMap.java @@ -2757,7 +2758,7 @@ sun/misc/Ref.java ../../classpath/gnu/xml/dom/DomNotation.java ../../classpath/gnu/xml/dom/DomNsNode.java ../../classpath/gnu/xml/dom/DomNSResolverContext.java -../../classpath/gnu/xml/dom/DomPI.java +../../classpath/gnu/xml/dom/DomProcessingInstruction.java ../../classpath/gnu/xml/dom/DomText.java ../../classpath/gnu/xml/dom/DomXPathExpression.java ../../classpath/gnu/xml/dom/DomXPathNSResolver.java @@ -2767,7 +2768,7 @@ sun/misc/Ref.java ../../classpath/gnu/xml/dom/ImplementationList.java ../../classpath/gnu/xml/dom/ImplementationSource.java ../../classpath/gnu/xml/dom/JAXPFactory.java -../../classpath/gnu/xml/dom/ls/DomLSEx.java +../../classpath/gnu/xml/dom/ls/DomLSException.java ../../classpath/gnu/xml/dom/ls/DomLSInput.java ../../classpath/gnu/xml/dom/ls/DomLSOutput.java ../../classpath/gnu/xml/dom/ls/DomLSParser.java diff --git a/classpath/classpath.build b/classpath/classpath.build index c84eb9b6..1dca3f0c 100644 --- a/classpath/classpath.build +++ b/classpath/classpath.build @@ -37,7 +37,7 @@ - + diff --git a/classpath/java/lang/VMClassLoader.java b/classpath/java/lang/VMClassLoader.java index 3d778cda..dd59de9c 100644 --- a/classpath/java/lang/VMClassLoader.java +++ b/classpath/java/lang/VMClassLoader.java @@ -174,37 +174,34 @@ final class VMClassLoader */ static Enumeration getResources(String name) throws IOException { - synchronized(nestedGetResourcesHack) - { - if(cli.System.Threading.Thread.GetData(nestedGetResourcesHack) != null) + if(__tls_nestedGetResourcesHack) + { + return gnu.java.util.EmptyEnumeration.getInstance(); + } + __tls_nestedGetResourcesHack = true; + try + { + Assembly[] assemblies = findResourceAssemblies(name); + java.util.Vector v = new java.util.Vector(); + for(int i = 0; i < assemblies.length; i++) { - return gnu.java.util.EmptyEnumeration.getInstance(); + v.addElement(new URL("ikvmres", assemblies[i].get_FullName(), -1, "/" + name)); } - cli.System.Threading.Thread.SetData(nestedGetResourcesHack, ""); - try + Enumeration e = v.elements(); + ClassLoader bootstrap = getBootstrapClassLoader(); + if(bootstrap != null) { - Assembly[] assemblies = findResourceAssemblies(name); - java.util.Vector v = new java.util.Vector(); - for(int i = 0; i < assemblies.length; i++) - { - v.addElement(new URL("ikvmres", assemblies[i].get_FullName(), -1, "/" + name)); - } - Enumeration e = v.elements(); - ClassLoader bootstrap = getBootstrapClassLoader(); - if(bootstrap != null) - { - e = new DoubleEnumeration(e, bootstrap.getResources(name)); - } - return e; + e = new DoubleEnumeration(e, bootstrap.getResources(name)); } - finally - { - cli.System.Threading.Thread.SetData(nestedGetResourcesHack, null); - } - } + return e; + } + finally + { + __tls_nestedGetResourcesHack = false; + } } - private static cli.System.LocalDataStoreSlot nestedGetResourcesHack = cli.System.Threading.Thread.AllocateDataSlot(); + private static boolean __tls_nestedGetResourcesHack; /** * Helper to get a package from the bootstrap class loader. The default @@ -252,15 +249,9 @@ final class VMClassLoader if(packages == null) { ClassLoader boot = getBootstrapClassLoader(); - if(boot != null) + if(boot != null && __tls_nestedGetResourcesHack) { - synchronized(nestedGetResourcesHack) - { - if(cli.System.Threading.Thread.GetData(nestedGetResourcesHack) != null) - { - return new Package[0]; - } - } + return new Package[0]; } HashMap h = new HashMap(); Assembly[] assemblies = AppDomain.get_CurrentDomain().GetAssemblies(); @@ -283,17 +274,14 @@ final class VMClassLoader if(boot != null) { Package[] pkgboot; - synchronized(nestedGetResourcesHack) + __tls_nestedGetResourcesHack = true; + try { - cli.System.Threading.Thread.SetData(nestedGetResourcesHack, ""); - try - { - pkgboot = boot.getPackages(); - } - finally - { - cli.System.Threading.Thread.SetData(nestedGetResourcesHack, null); - } + pkgboot = boot.getPackages(); + } + finally + { + __tls_nestedGetResourcesHack = false; } Collection c = h.values(); packages = new Package[c.size() + pkgboot.length]; diff --git a/classpath/java/lang/VMThread.java b/classpath/java/lang/VMThread.java index e406f528..cf90ef85 100644 --- a/classpath/java/lang/VMThread.java +++ b/classpath/java/lang/VMThread.java @@ -6,7 +6,8 @@ final class VMThread { private static final Object countLock = new Object(); private static int nonDaemonCount; - private static final cli.System.LocalDataStoreSlot localDataStoreSlot = cli.System.Threading.Thread.AllocateDataSlot(); + private static Thread __tls_javaThread; + private static Object __tls_cleanup; private cli.System.WeakReference nativeThreadReference; // Note: when this thread dies, this reference is *not* cleared @@ -122,7 +123,8 @@ final class VMThread synchronized(vmthread) { vmthread.cleanup(); - cli.System.Threading.Thread.SetData(localDataStoreSlot, null); + __tls_javaThread = null; + __tls_cleanup = null; VMThread joinWaiter = vmthread.firstJoinWaiter; while(joinWaiter != null) { @@ -163,8 +165,7 @@ final class VMThread static void setThreadGroup(ThreadGroup group) { - Thread javaThread = (Thread)cli.System.Threading.Thread.GetData(localDataStoreSlot); - if(javaThread == null) + if(__tls_javaThread == null) { newThread(group); } @@ -283,7 +284,7 @@ final class VMThread { public void Invoke() { - cli.System.Threading.Thread.SetData(localDataStoreSlot, thread); + __tls_javaThread = thread; run(); } }); @@ -508,8 +509,8 @@ final class VMThread } } vmThread.thread = javaThread; - cli.System.Threading.Thread.SetData(localDataStoreSlot, javaThread); - cli.System.Threading.Thread.SetData(cli.System.Threading.Thread.GetNamedDataSlot("ikvm-thread-hack"), new CleanupHack(javaThread)); + __tls_javaThread = javaThread; + __tls_cleanup = new CleanupHack(javaThread); javaThread.group = group; javaThread.group.addThread(javaThread); InheritableThreadLocal.newChildThread(javaThread); @@ -518,10 +519,10 @@ final class VMThread static Thread currentThread() { - Thread javaThread = (Thread)cli.System.Threading.Thread.GetData(localDataStoreSlot); + Thread javaThread = __tls_javaThread; if(javaThread == null) { - javaThread = newThread(ThreadGroup.root); + __tls_javaThread = javaThread = newThread(ThreadGroup.root); } return javaThread; } diff --git a/classpath/java/security/VMAccessController.java b/classpath/java/security/VMAccessController.java index c8183e02..f55eb7bd 100644 --- a/classpath/java/security/VMAccessController.java +++ b/classpath/java/security/VMAccessController.java @@ -57,14 +57,14 @@ final class VMAccessController * call stack. We use this to remember which context object corresponds to * which call. */ - private static final cli.System.LocalDataStoreSlot contexts = cli.System.Threading.Thread.AllocateDataSlot(); + private static LinkedList __tls_contexts; /** * This is a Boolean that, if set, tells getContext that it has already * been called once, allowing us to handle recursive permission checks * caused by methods getContext calls. */ - private static final cli.System.LocalDataStoreSlot inGetContext = cli.System.Threading.Thread.AllocateDataSlot(); + private static boolean __tls_inGetContext; /** * And we return this all-permissive context to ensure that privileged @@ -111,11 +111,11 @@ final class VMAccessController { if (DEBUG) debug("pushing " + acc); - LinkedList stack = (LinkedList) cli.System.Threading.Thread.GetData(contexts); + LinkedList stack = __tls_contexts; if (stack == null) { stack = new LinkedList(); - cli.System.Threading.Thread.SetData(contexts, stack); + __tls_contexts = stack; } stack.addFirst(acc); } @@ -133,12 +133,12 @@ final class VMAccessController // Stack should never be null, nor should it be empty, if this method // and its counterpart has been called properly. - LinkedList stack = (LinkedList) cli.System.Threading.Thread.GetData(contexts); + LinkedList stack = __tls_contexts; if (stack != null) { stack.removeFirst(); if (stack.isEmpty()) - cli.System.Threading.Thread.SetData(contexts, null); + __tls_contexts = null; } } @@ -157,15 +157,14 @@ final class VMAccessController // // XXX is this necessary? We should verify if there are any calls in // the stack below this method that require permission checks. - Boolean inCall = (Boolean) cli.System.Threading.Thread.GetData(inGetContext); - if (inCall != null && inCall.booleanValue()) + if (__tls_inGetContext) { if (DEBUG) debug("already in getContext"); return DEFAULT_CONTEXT; } - cli.System.Threading.Thread.SetData(inGetContext, Boolean.TRUE); + __tls_inGetContext = true; Object[][] stack = getStack(); Class[] classes = (Class[]) stack[0]; @@ -210,7 +209,7 @@ final class VMAccessController { // If there was a call to doPrivileged with a supplied context, // return that context. - LinkedList l = (LinkedList) cli.System.Threading.Thread.GetData(contexts); + LinkedList l = __tls_contexts; if (l != null) context = (AccessControlContext) l.getFirst(); privileged = 1; @@ -245,7 +244,7 @@ final class VMAccessController else context = new AccessControlContext(result); - cli.System.Threading.Thread.SetData(inGetContext, Boolean.FALSE); + __tls_inGetContext = false; return context; } diff --git a/ikvmc/Compiler.cs b/ikvmc/Compiler.cs index b61d636d..2b94ea6f 100644 --- a/ikvmc/Compiler.cs +++ b/ikvmc/Compiler.cs @@ -120,6 +120,8 @@ class Compiler Console.Error.WriteLine(" -Xtrace: Displays all tracepoints with the given name"); Console.Error.WriteLine(" -Xmethodtrace: Build tracing into the specified output methods"); Console.Error.WriteLine(" -monoBugWorkaround Workaround metadata bug in Mono 1.0.5 and 1.1.3"); + Console.Error.WriteLine(" -enabletls Apply ThreadStaticAttribute to fields starting"); + Console.Error.WriteLine(" with __tls_"); return 1; } foreach(string s in arglist) @@ -359,6 +361,10 @@ class Compiler { options.monoBugWorkaround = true; } + else if(s == "-enabletls") + { + options.enableTls = true; + } else if(s == "-opt:fields") { options.removeUnusedFields = true; diff --git a/runtime/JniInterface.cs b/runtime/JniInterface.cs index baa50b3d..14a98e79 100644 --- a/runtime/JniInterface.cs +++ b/runtime/JniInterface.cs @@ -370,16 +370,22 @@ namespace IKVM.Runtime sealed class JniHelper { + //[DllImport("ikvm-native", EntryPoint="_ikvm_LoadLibrary@4")] [DllImport("ikvm-native")] private static extern IntPtr ikvm_LoadLibrary(string filename); + //[DllImport("ikvm-native", EntryPoint="_ikvm_FreeLibrary@4")] [DllImport("ikvm-native")] private static extern void ikvm_FreeLibrary(IntPtr handle); + //[DllImport("ikvm-native", EntryPoint="_ikvm_GetProcAddress@12")] [DllImport("ikvm-native")] internal static extern IntPtr ikvm_GetProcAddress(IntPtr handle, string name, int argc); + //[DllImport("ikvm-native", EntryPoint="_ikvm_CallOnLoad@12")] [DllImport("ikvm-native")] private unsafe static extern int ikvm_CallOnLoad(IntPtr method, void* jvm, void* reserved); + //[DllImport("ikvm-native", EntryPoint="_ikvm_GetJNIEnvVTable@0")] [DllImport("ikvm-native")] internal unsafe static extern void** ikvm_GetJNIEnvVTable(); + //[DllImport("ikvm-native", EntryPoint="_ikvm_MarshalDelegate@4")] [DllImport("ikvm-native")] internal unsafe static extern void* ikvm_MarshalDelegate(Delegate d); @@ -1639,13 +1645,18 @@ namespace IKVM.Runtime { TypeWrapper wrapper = IKVM.NativeCode.java.lang.VMClass.getWrapperFromClass(pEnv->UnwrapRef(clazz)); wrapper.Finish(); - MethodWrapper mw = wrapper.GetMethodWrapper(StringFromUTF8(name), StringFromUTF8(sig).Replace('/', '.'), true); - if(mw != null) + string methodsig = StringFromUTF8(sig); + // don't allow dotted names! + if(methodsig.IndexOf('.') < 0) { - if(mw.IsStatic == isstatic) + MethodWrapper mw = wrapper.GetMethodWrapper(StringFromUTF8(name), methodsig.Replace('/', '.'), true); + if(mw != null) { - mw.Link(); - return mw.Cookie; + if(mw.IsStatic == isstatic) + { + mw.Link(); + return mw.Cookie; + } } } SetPendingException(pEnv, JavaException.NoSuchMethodError("{0}{1}", StringFromUTF8(name), StringFromUTF8(sig).Replace('/', '.'))); @@ -1852,12 +1863,17 @@ namespace IKVM.Runtime { TypeWrapper wrapper = IKVM.NativeCode.java.lang.VMClass.getWrapperFromClass(pEnv->UnwrapRef(clazz)); wrapper.Finish(); - FieldWrapper fw = wrapper.GetFieldWrapper(StringFromUTF8(name), StringFromUTF8(sig).Replace('/', '.')); - if(fw != null) + string fieldsig = StringFromUTF8(sig); + // don't allow dotted names! + if(fieldsig.IndexOf('.') < 0) { - if(fw.IsStatic == isstatic) + FieldWrapper fw = wrapper.GetFieldWrapper(StringFromUTF8(name), fieldsig.Replace('/', '.')); + if(fw != null) { - return fw.Cookie; + if(fw.IsStatic == isstatic) + { + return fw.Cookie; + } } } SetPendingException(pEnv, JavaException.NoSuchFieldError(StringFromUTF8(name))); @@ -2872,8 +2888,14 @@ namespace IKVM.Runtime wrapper.Finish(); for(int i = 0; i < nMethods; i++) { - // TODO this won't work when we're putting the JNI methods in jniproxy.dll - FieldInfo fi = wrapper.TypeAsTBD.GetField(JNI.METHOD_PTR_FIELD_PREFIX + StringFromUTF8(methods[i].name) + StringFromUTF8(methods[i].signature).Replace('/', '.') + ">", BindingFlags.Static | BindingFlags.NonPublic); + string methodsig = StringFromUTF8(methods[i].signature); + FieldInfo fi = null; + // don't allow dotted names! + if(methodsig.IndexOf('.') < 0) + { + // TODO this won't work when we're putting the JNI methods in jniproxy.dll + fi = wrapper.TypeAsTBD.GetField(JNI.METHOD_PTR_FIELD_PREFIX + StringFromUTF8(methods[i].name) + methodsig.Replace('/', '.') + ">", BindingFlags.Static | BindingFlags.NonPublic); + } if(fi == null) { SetPendingException(pEnv, JavaException.NoSuchMethodError(StringFromUTF8(methods[i].name))); @@ -3139,7 +3161,8 @@ namespace IKVM.Runtime { for(int i = 0; i < GlobalRefs.weakRefs.Length; i++) { - if(!GlobalRefs.weakRefs[i].IsAllocated) + // MONOBUG GCHandle.IsAllocated is horribly broken, so we also check the value of the handle + if(!GlobalRefs.weakRefs[i].IsAllocated || (IntPtr)GlobalRefs.weakRefs[i] == IntPtr.Zero) { GlobalRefs.weakRefs[i] = GCHandle.Alloc(o, GCHandleType.WeakTrackResurrection); return (IntPtr)(- (i | (1 << 30))); diff --git a/runtime/MemberWrapper.cs b/runtime/MemberWrapper.cs index b389aac3..80dd76af 100644 --- a/runtime/MemberWrapper.cs +++ b/runtime/MemberWrapper.cs @@ -74,7 +74,8 @@ class MemberWrapper { lock(this) { - if(!handle.IsAllocated) + // MONOBUG GCHandle.IsAllocated is horribly broken, so we also check the value of the handle + if(!handle.IsAllocated || (IntPtr)handle == IntPtr.Zero) { handle = System.Runtime.InteropServices.GCHandle.Alloc(this, System.Runtime.InteropServices.GCHandleType.Weak); } diff --git a/runtime/TypeWrapper.cs b/runtime/TypeWrapper.cs index 027b4f59..7090fc15 100644 --- a/runtime/TypeWrapper.cs +++ b/runtime/TypeWrapper.cs @@ -89,9 +89,9 @@ class EmitHelper ilgen.Emit(OpCodes.Isinst, type); ilgen.Emit(OpCodes.Dup); Label ok = ilgen.DefineLabel(); - ilgen.Emit(OpCodes.Brtrue, ok); + ilgen.Emit(OpCodes.Brtrue_S, ok); ilgen.Emit(OpCodes.Ldloc, lb); - ilgen.Emit(OpCodes.Brfalse, ok); // handle null + ilgen.Emit(OpCodes.Brfalse_S, ok); // handle null ilgen.Emit(OpCodes.Ldtoken, type); ilgen.Emit(OpCodes.Ldloc, lb); ilgen.Emit(OpCodes.Call, verboseCastFailure); @@ -102,6 +102,23 @@ class EmitHelper ilgen.Emit(OpCodes.Castclass, type); } } + + // This is basically the same as Castclass, except that it + // throws an IncompatibleClassChangeError on failure. + internal static void EmitAssertType(ILGenerator ilgen, Type type) + { + LocalBuilder lb = ilgen.DeclareLocal(typeof(object)); + ilgen.Emit(OpCodes.Stloc, lb); + ilgen.Emit(OpCodes.Ldloc, lb); + ilgen.Emit(OpCodes.Isinst, type); + ilgen.Emit(OpCodes.Dup); + Label ok = ilgen.DefineLabel(); + ilgen.Emit(OpCodes.Brtrue_S, ok); + ilgen.Emit(OpCodes.Ldloc, lb); + ilgen.Emit(OpCodes.Brfalse_S, ok); // handle null + EmitHelper.Throw(ilgen, "java.lang.IncompatibleClassChangeError"); + ilgen.MarkLabel(ok); + } } class AttributeHelper @@ -1391,7 +1408,8 @@ abstract class TypeWrapper // for any interface reference else if(IsInterfaceOrInterfaceArray && (sourceType == null || sourceType.IsUnloadable || !sourceType.IsAssignableTo(this))) { - ilgen.Emit(OpCodes.Castclass, TypeAsTBD); + EmitHelper.EmitAssertType(ilgen, TypeAsTBD); + Profiler.Count("InterfaceDownCast"); } else if(IsNonPrimitiveValueType) { @@ -2720,6 +2738,11 @@ sealed class DynamicTypeWrapper : TypeWrapper setModifiers = true; } field = typeBuilder.DefineField(fieldName, type, attribs); + if(JVM.IsTlsEnabled && fieldName.StartsWith("__tls_")) + { + CustomAttributeBuilder threadStaticAttrib = new CustomAttributeBuilder(typeof(ThreadStaticAttribute).GetConstructor(Type.EmptyTypes), new object[0]); + field.SetCustomAttribute(threadStaticAttrib); + } if(fld.IsTransient) { CustomAttributeBuilder transientAttrib = new CustomAttributeBuilder(typeof(NonSerializedAttribute).GetConstructor(Type.EmptyTypes), new object[0]); diff --git a/runtime/compiler.cs b/runtime/compiler.cs index 3e5311c1..9f1a0314 100644 --- a/runtime/compiler.cs +++ b/runtime/compiler.cs @@ -1987,6 +1987,9 @@ class Compiler } else { + // NOTE for verifiability it is expressly *not* required that the + // value matches the array type, so we don't need to handle interface + // references here. ilGenerator.Emit(OpCodes.Stelem_Ref); } } @@ -2789,8 +2792,7 @@ class Compiler TypeWrapper tw = ma.GetRawStackTypeWrapper(instructionIndex, args.Length - 1 - i); if(tw.IsUnloadable || (args[i].IsInterfaceOrInterfaceArray && !tw.IsAssignableTo(args[i]))) { - // TODO ideally, instead of an InvalidCastException, the castclass should throw a IncompatibleClassChangeError - ilGenerator.Emit(OpCodes.Castclass, args[i].TypeAsTBD); + EmitHelper.EmitAssertType(ilGenerator, args[i].TypeAsTBD); Profiler.Count("InterfaceDownCast"); } } diff --git a/runtime/remapper.cs b/runtime/remapper.cs index 7ae1ac9f..08fb771f 100644 --- a/runtime/remapper.cs +++ b/runtime/remapper.cs @@ -145,7 +145,7 @@ namespace IKVM.Internal.MapXml } if(tw.IsGhost) { - tw.EmitConvStackTypeToSignatureType(ilgen, tw); + tw.EmitConvStackTypeToSignatureType(ilgen, null); } temps[j] = ilgen.DeclareLocal(tw.TypeAsSignatureType); ilgen.Emit(OpCodes.Stloc, temps[j]); diff --git a/runtime/vm.cs b/runtime/vm.cs index 8feb7966..c83a858b 100644 --- a/runtime/vm.cs +++ b/runtime/vm.cs @@ -45,12 +45,8 @@ namespace IKVM.Runtime { } - public static string[] Glob(string arg) + private static string[] Glob(string arg) { - if(IKVM.Internal.JVM.IsUnix) - { - return new string[] { arg }; - } try { string dir = Path.GetDirectoryName(arg); @@ -77,14 +73,7 @@ namespace IKVM.Runtime public static string[] Glob() { - if(IKVM.Internal.JVM.IsUnix) - { - return Environment.GetCommandLineArgs(); - } - else - { - return Glob(1); - } + return Glob(1); } public static string[] Glob(int skip) @@ -163,7 +152,13 @@ namespace IKVM.Runtime { if(Thread.CurrentThread.Name == null) { - Thread.CurrentThread.Name = "main"; + try + { + Thread.CurrentThread.Name = "main"; + } + catch(InvalidOperationException) + { + } } } @@ -172,17 +167,9 @@ namespace IKVM.Runtime // FXBUG when the main thread ends, it doesn't actually die, it stays around to manage the lifetime // of the CLR, but in doing so it also keeps alive the thread local storage for this thread and we // use the TLS as a hack to track when the thread dies (if the object stored in the TLS is finalized, - // we know the thread is dead). So to make that work for the main thread, we explicitly clear the TLS - // slot that contains our hack object. - try - { - Thread.SetData(Thread.GetNamedDataSlot("ikvm-thread-hack"), null); - } - catch(NullReferenceException) - { - // MONOBUG Thread.SetData throws a NullReferenceException on Mono - // if the slot hadn't already been allocated - } + // we know the thread is dead). So to make that work for the main thread, we use jniDetach which + // explicitly cleans up our thread. + IKVM.Internal.JVM.Library.jniDetach(); } } @@ -296,6 +283,7 @@ namespace IKVM.Internal private static bool noJniStubs; private static bool isStaticCompiler; private static bool noStackTraceInfo; + private static bool isTlsEnabled; private static bool compilationPhase1; private static string sourcePath; private static bool monoBugWorkaround; @@ -381,6 +369,18 @@ namespace IKVM.Internal } } + internal static bool IsTlsEnabled + { + get + { + return isTlsEnabled; + } + set + { + isTlsEnabled = value; + } + } + internal static bool CompileInnerClassesAsNestedTypes { get @@ -1948,6 +1948,7 @@ namespace IKVM.Internal public bool nostacktraceinfo; public bool removeUnusedFields; public bool monoBugWorkaround; + public bool enableTls; } public static int Compile(CompilerOptions options) @@ -1957,6 +1958,7 @@ namespace IKVM.Internal noJniStubs = options.nojni; noStackTraceInfo = options.nostacktraceinfo; monoBugWorkaround = options.monoBugWorkaround; + isTlsEnabled = options.enableTls; foreach(string r in options.references) { try