Directory Roadmap
=================
+* `acceptance-tests/` - Optional third party test suites used to validate Mono against a wider range of test cases.
+
* `data/` - Configuration files installed as part of the Mono runtime.
* `docs/` - Technical documents about the Mono runtime.
{
"name": "ms-test-suite",
"url": "git@github.com:xamarin/ms-test-suite.git",
- "rev": "1ab0d4972937bf0451fc164f39a67475776a9edf",
+ "rev": "4a95604fdf2c0523e68eaad64a17d22eebb051b3",
"remote-branch": "origin/master",
"branch": "master",
"directory": "ms-test-suite"
check-ms-test-suite:
@if $(MAKE) validate-ms-test-suite RESET_VERSIONS=1; then \
- $(MAKE) -C $(MSTESTSUITE_PATH)/conformance build MCS="$(MCS) -t:library -warn:1 -r:nunit.framework"; \
- $(MAKE) -C $(MSTESTSUITE_PATH)/conformance run NUNIT-CONSOLE="$(RUNTIME) $(CLASS)/nunit-console.exe -nologo -exclude=MonoBug,BadTest" NUNIT_XML_RESULT=$(abs_top_builddir)/acceptance-tests/TestResult-ms-test-suite-conformance.xml; \
- $(MAKE) -C $(MSTESTSUITE_PATH)/systemruntimebringup build MCS="$(MCS) -debug -warn:1"; \
- $(MAKE) -C $(MSTESTSUITE_PATH)/systemruntimebringup run MONO="$(RUNTIME)"; \
+ $(MAKE) -C $(MSTESTSUITE_PATH)/conformance build MCS="$(MCS) -t:library -warn:1 -r:nunit.framework" && \
+ $(MAKE) -C $(MSTESTSUITE_PATH)/conformance run NUNIT-CONSOLE="$(RUNTIME) $(CLASS)/nunit-console.exe -nologo -exclude=MonoBug,BadTest" NUNIT_XML_RESULT=$(abs_top_builddir)/acceptance-tests/TestResult-ms-test-suite-conformance.xml || EXIT_CODE=1; \
+ $(MAKE) -C $(MSTESTSUITE_PATH)/systemruntimebringup build MCS="$(MCS) -debug -warn:1" && \
+ $(MAKE) -C $(MSTESTSUITE_PATH)/systemruntimebringup run MONO="$(RUNTIME)" || EXIT_CODE=1; \
+ exit $$EXIT_CODE; \
else \
echo "*** [ms-test-suite] Getting the repository failed, you probably don't have access to this Xamarin-internal resource. Skipping."; \
fi
\ No newline at end of file
cd $(ROSLYN_PATH); \
sed -i -e 'N; s/bootstrapArg=".*\n.*"/bootstrapArg=""/g' cibuild.sh; \
sed -i -e 's#-xml Binaries/\$$BUILD_CONFIGURATION/xUnitResults/#-nunit $(abs_top_builddir)/acceptance-tests/TestResult-#g' cibuild.sh; \
- ./cibuild.sh --mono-path $$PREFIX/bin; \
- sed -i -e 's/\\4.5"/\\4.5-api"/g' $$PREFIX/lib/mono/xbuild-frameworks/.NETFramework/v4.5/RedistList/FrameworkList.xml;
+ ./cibuild.sh --mono-path $$PREFIX/bin || EXIT_CODE=1; \
+ sed -i -e 's/\\4.5"/\\4.5-api"/g' $$PREFIX/lib/mono/xbuild-frameworks/.NETFramework/v4.5/RedistList/FrameworkList.xml; \
+ exit $$EXIT_CODE
fi
], [with_cooperative_gc=no])
-AC_ARG_WITH(checked_build, [ --with-checked-build=yes|no Enable checked build (expensive asserts)) (defaults to no)],[
- if test x$with_checked_build != xno ; then
- AC_DEFINE(CHECKED_BUILD,1,[Enable checked build.])
- fi
-], [with_checked_build=no])
+AC_ARG_ENABLE(checked_build, [ --enable-checked-build=LIST To enable checked build (expensive asserts), configure with a comma-separated LIST of checked build modules and then include that same list in the environment variable MONO_CHECK_MODE at runtime. Recognized checked build modules: all, gc, metadata, thread],[
-if test x$with_checked_build != xno ; then
- DISABLED_CHECKED_BUILD_TEST=none
+ if test x$enable_checked_build != x ; then
+ AC_DEFINE(ENABLE_CHECKED_BUILD,1,[Enable checked build])
+ fi
+ for feature in `echo "$enable_checked_build" | sed -e "s/,/ /g"`; do
+ eval "mono_checked_build_test_enable_$feature='yes'"
+ done
- AC_ARG_ENABLE(checked_build_test, [ --enable-checked-build-test=LIST drop support for LIST checked build tests. LIST is a comma-separated list from: gc, metadata, thread.],
- [
- for feature in `echo "$enable_checked_build_test" | sed -e "s/,/ /g"`; do
- eval "mono_checked_build_test_disable_$feature='yes'"
- done
- DISABLED_CHECKED_BUILD_TEST=$enable_checked_build_test
- ],[])
+ if test "x$mono_checked_build_test_enable_all" = "xyes"; then
+ eval "mono_checked_build_test_enable_gc='yes'"
+ eval "mono_checked_build_test_enable_metadata='yes'"
+ eval "mono_checked_build_test_enable_thread='yes'"
+ fi
- if test "x$mono_checked_build_test_disable_gc" = "xyes"; then
- AC_DEFINE(DISABLE_CHECKED_BUILD_GC, 1, [Disable GC checked build])
+ if test "x$mono_checked_build_test_enable_gc" = "xyes"; then
+ AC_DEFINE(ENABLE_CHECKED_BUILD_GC, 1, [Enable GC checked build])
fi
- if test "x$mono_checked_build_test_disable_metadata" = "xyes"; then
- AC_DEFINE(DISABLE_CHECKED_BUILD_METADATA, 1, [Disable metadata checked build])
+ if test "x$mono_checked_build_test_enable_metadata" = "xyes"; then
+ AC_DEFINE(ENABLE_CHECKED_BUILD_METADATA, 1, [Enable metadata checked build])
fi
- if test "x$mono_checked_build_test_disable_thread" = "xyes"; then
- AC_DEFINE(DISABLE_CHECKED_BUILD_THREAD, 1, [Disable thread checked build])
+ if test "x$mono_checked_build_test_enable_thread" = "xyes"; then
+ AC_DEFINE(ENABLE_CHECKED_BUILD_THREAD, 1, [Enable thread checked build])
fi
-fi
+
+], [])
AC_CHECK_HEADER([malloc.h],
[AC_DEFINE([HAVE_USR_INCLUDE_MALLOC_H], [1],
-Subproject commit 35b7594b5498e9712522e0556ec290415691dddd
+Subproject commit 6c6e36218c4a0b6dfb85bd27fa6746467761e8a0
this mode can be enabled at compile time by using the --with-cooperative-gc
flag when calling configure.
.TP
-\fBMONO_ENABLE_SHM\fR
-Unix only: Enable support for cross-process handles. Cross-process
-handles are used to expose process handles, thread handles, named
-mutexes, named events and named semaphores across Unix processes.
-.TP
\fBMONO_ENV_OPTIONS\fR
This environment variable allows you to pass command line arguments to
a Mono process through the environment. This is useful for example
Locale.cs \
MonoTODOAttribute.cs \
basic-profile-check.cs \
- SR.cs
+ SR.cs \
+ AssemblyRef.cs
DISTFILES = \
README.makefiles \
rm -f $outfile.inc
-if test -n "$excfile"; then
+if test -n "$excfile" -a -f "$excfile"; then
process_includes $excfile $outfile.exc
fi
System.XML \
System Mono.Security \
Mono.Posix \
- System.Core
+ System.Core \
+ Mono.Cecil \
+ Mono.Cecil.Mdb
pcl_facade_dirs := Facades
public void Dispose ()
{
- WaitHandle.WaitAll (submissions.Select (s => s.WaitHandle).ToArray ());
+ if (submissions.Count > 0)
+ WaitHandle.WaitAll (submissions.Select (s => s.WaitHandle).ToArray ());
BuildNodeManager.Stop ();
}
};
var requestData = new BuildRequestData (this, targets ?? DefaultTargets.ToArray ());
var result = manager.Build (parameters, requestData);
+ manager.Dispose ();
targetOutputs = result.ResultsByTarget;
return result.OverallResult == BuildResultCode.Success;
}
public BuildNodeManager (BuildManager buildManager)
{
BuildManager = buildManager;
- new Thread (RunLoop).Start ();
+ new Thread (RunLoop) {
+ IsBackground = true,
+ Name = "xbuild request handler"
+ }.Start ();
}
~BuildNodeManager ()
#
# Update this comment to trigger a build in System
-# +2
+# +3
#
*/
public virtual bool InvokeSystemCertificateValidator (
ICertificateValidator validator, string targetHost, bool serverMode,
- X509CertificateCollection certificates, X509Chain chain, out bool success,
+ X509CertificateCollection certificates, ref X509Chain chain, out bool success,
ref MonoSslPolicyErrors errors, ref int status11)
{
success = false;
LIBRARY = System.IO.Compression.FileSystem.dll
LIB_REFS = System System.IO.Compression
LIB_MCS_FLAGS =
-TEST_MCS_FLAGS = /r:System /r:System.Core /r:System.IO.Compression.dll
+TEST_MCS_FLAGS = /r:System.dll /r:System.Core.dll /r:System.IO.Compression.dll
include ../../build/library.make
LIBRARY = System.IO.Compression.dll
LIB_REFS = System System.Core
LIB_MCS_FLAGS = /unsafe
-TEST_MCS_FLAGS = /r:System /r:System.Core
+TEST_MCS_FLAGS = /r:System.dll /r:System.Core.dll
include ../../build/library.make
const int WaitTimeout = 5000;
- string port, TestHost, LocalServer;
+ string TestHost, LocalServer;
+ int port;
[SetUp]
public void SetupFixture ()
{
if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
- port = "810";
+ port = 810;
} else {
- port = "8810";
+ port = 8810;
}
TestHost = "localhost:" + port;
var response = l.Response;
response.StatusCode = (int)HttpStatusCode.Moved;
- response.RedirectLocation = "http://xamarin.com/";
+ response.RedirectLocation = "http://localhost:8811/";
});
+ var listener2 = CreateListener (l => {
+ var response = l.Response;
+
+ response.StatusCode = (int)HttpStatusCode.OK;
+ response.OutputStream.WriteByte (0x68);
+ response.OutputStream.WriteByte (0x65);
+ response.OutputStream.WriteByte (0x6c);
+ response.OutputStream.WriteByte (0x6c);
+ response.OutputStream.WriteByte (0x6f);
+ }, 8811);
+
try {
var chandler = new HttpClientHandler ();
chandler.AllowAutoRedirect = true;
var r = client.GetAsync (LocalServer);
Assert.IsTrue (r.Wait (WaitTimeout), "#1");
var resp = r.Result;
- Assert.AreEqual ("http://xamarin.com/", resp.RequestMessage.RequestUri.AbsoluteUri, "#2");
+ Assert.AreEqual ("http://localhost:8811/", resp.RequestMessage.RequestUri.AbsoluteUri, "#2");
+ Assert.AreEqual ("hello", resp.Content.ReadAsStringAsync ().Result, "#3");
} finally {
listener.Abort ();
listener.Close ();
+ listener2.Abort ();
+ listener2.Close ();
}
}
}
HttpListener CreateListener (Action<HttpListenerContext> contextAssert)
+ {
+ return CreateListener (contextAssert, port);
+ }
+
+ HttpListener CreateListener (Action<HttpListenerContext> contextAssert, int port)
{
var l = new HttpListener ();
l.Prefixes.Add (string.Format ("http://+:{0}/", port));
LIBRARY = System.Runtime.Remoting.dll
-LIB_REFS = System System.Web System.Xml System.Runtime.Serialization.Formatters.Soap
+LIB_REFS = System System.Xml System.Runtime.Serialization.Formatters.Soap
LIB_MCS_FLAGS = /r:$(corlib)
+ifndef NO_SYSTEM_WEB_DEPENDENCY
+LIB_REFS += System.Web
+endif
+
TEST_MCS_FLAGS = $(LIB_MCS_FLAGS) -nowarn:618 /r:System.Runtime.Remoting.dll
TEST_MONO_PATH = .
--- /dev/null
+Assembly/AssemblyInfo.cs
+../../build/common/Consts.cs
+../../build/common/Locale.cs
+../../build/common/MonoTODOAttribute.cs
+System.Runtime.Remoting.Channels/BinaryClientFormatterSink.cs
+System.Runtime.Remoting.Channels/BinaryClientFormatterSinkProvider.cs
+System.Runtime.Remoting.Channels/BinaryCore.cs
+System.Runtime.Remoting.Channels/BinaryServerFormatterSink.cs
+System.Runtime.Remoting.Channels/BinaryServerFormatterSinkProvider.cs
+System.Runtime.Remoting.Channels/ChannelCore.cs
+System.Runtime.Remoting.Channels/CommonTransportKeys.cs
+System.Runtime.Remoting.Channels/IAuthorizeRemotingConnection.cs
+System.Runtime.Remoting.Channels/RemotingThreadPool.cs
+System.Runtime.Remoting.Channels/SoapClientFormatterSink.cs
+System.Runtime.Remoting.Channels/SoapCore.cs
+System.Runtime.Remoting.Channels/SoapServerFormatterSink.cs
+System.Runtime.Remoting.Channels/SoapClientFormatterSinkProvider.cs
+System.Runtime.Remoting.Channels/SoapServerFormatterSinkProvider.cs
+System.Runtime.Remoting.Channels/SoapMessageFormatter.cs
+System.Runtime.Remoting.Channels/SocketCachePolicy.cs
+System.Runtime.Remoting.Channels.Ipc/IpcChannel.cs
+System.Runtime.Remoting.Channels.Ipc/IpcClientChannel.cs
+System.Runtime.Remoting.Channels.Ipc/IpcServerChannel.cs
+System.Runtime.Remoting.Channels.Ipc.Win32/IpcTransport.cs
+System.Runtime.Remoting.Channels.Ipc.Win32/IpcChannel.cs
+System.Runtime.Remoting.Channels.Ipc.Win32/IpcChannelHelper.cs
+System.Runtime.Remoting.Channels.Ipc.Win32/IpcClientChannel.cs
+System.Runtime.Remoting.Channels.Ipc.Win32/IpcServerChannel.cs
+System.Runtime.Remoting.Channels.Ipc.Win32/NamedPipeClient.cs
+System.Runtime.Remoting.Channels.Ipc.Win32/NamedPipeException.cs
+System.Runtime.Remoting.Channels.Ipc.Win32/NamedPipeHelper.cs
+System.Runtime.Remoting.Channels.Ipc.Win32/NamedPipeListener.cs
+System.Runtime.Remoting.Channels.Ipc.Win32/NamedPipeSocket.cs
+System.Runtime.Remoting.Channels.Ipc.Win32/NamedPipeStream.cs
+System.Runtime.Remoting.Channels.Ipc.Unix/IpcChannel.cs
+System.Runtime.Remoting.Channels.Ipc.Unix/IpcClientChannel.cs
+System.Runtime.Remoting.Channels.Ipc.Unix/IpcServerChannel.cs
+System.Runtime.Remoting.Channels.Ipc.Unix/UnixChannelLoader.cs
+System.Runtime.Remoting.Channels.Tcp/TcpChannel.cs
+System.Runtime.Remoting.Channels.Tcp/TcpClientChannel.cs
+System.Runtime.Remoting.Channels.Tcp/TcpMessageIO.cs
+System.Runtime.Remoting.Channels.Tcp/TcpServerChannel.cs
+System.Runtime.Remoting.Channels.Tcp/TcpServerTransportSink.cs
+System.Runtime.Remoting.Channels.Tcp/TcpClientTransportSinkProvider.cs
+System.Runtime.Remoting.Channels.Tcp/TcpClientTransportSink.cs
+System.Runtime.Remoting.Channels.Tcp/TcpConnectionPool.cs
+System.Runtime.Remoting.MetadataServices/MetaData.cs
+System.Runtime.Remoting.MetadataServices/MetaDataExporter.cs
+System.Runtime.Remoting.MetadataServices/MetaDataCodeGenerator.cs
+System.Runtime.Remoting.MetadataServices/SdlChannelSinkProvider.cs
+System.Runtime.Remoting.MetadataServices/ServiceType.cs
+System.Runtime.Remoting.MetadataServices/SUDSParserException.cs
+System.Runtime.Remoting.MetadataServices/SdlChannelSink.cs
+System.Runtime.Remoting.MetadataServices/SdlType.cs
+System.Runtime.Remoting.MetadataServices/SUDSGeneratorException.cs
+System.Runtime.Remoting.Services/RemotingClientProxy.cs
if (!hashAlg.CanReuseTransform) {
canReuseHashAlg = false;
hashAlg = null;
+ return null;
}
+ hashAlg.Key = MachineKeySectionUtils.GetValidationKey (mks);
}
if (hashAlg != null)
if (provider != null && provider.HasCustomSystemCertificateValidator) {
var xerrors = (MonoSslPolicyErrors)errors;
var xchain = (XX509Chain)(object)chain;
- providerValidated = provider.InvokeSystemCertificateValidator (this, host, server, certs, xchain, out result, ref xerrors, ref status11);
+ providerValidated = provider.InvokeSystemCertificateValidator (this, host, server, certs, ref xchain, out result, ref xerrors, ref status11);
+ chain = (X509Chain)(object)xchain;
errors = (SslPolicyErrors)xerrors;
}
static bool CheckUsage (XX509CertificateCollection certs, string host, ref SslPolicyErrors errors, ref int status11)
{
#if !MONOTOUCH
- var leaf = (X509Certificate2)certs[0];
+ var leaf = certs[0] as X509Certificate2;
+ if (leaf == null)
+ leaf = new X509Certificate2 (certs[0]);
// for OSX and iOS we're using the native API to check for the SSL server policy and host names
if (!is_macosx) {
if (!CheckCertificateUsage (leaf)) {
WritePrefix ();
}
- WriteDebugString (message);
-
if (Debugger.IsLogging())
Debugger.Log (0, null, message);
+ else
+ WriteDebugString (message);
WriteLogFile (message, LogFileName);
}
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
[MonitoringDescription ("The session ID for this process.")]
public int SessionId {
- get { throw new NotImplementedException (); }
+ get { return 0; }
}
[MethodImplAttribute(MethodImplOptions.InternalCall)]
sealed class SafeSocketHandle : SafeHandleZeroOrMinusOneIsInvalid {
List<Thread> blocking_threads;
+ bool in_cleanup;
const int SOCKET_CLOSED = 10004;
#endif
if (blocking_threads != null) {
- int abort_attempts = 0;
- while (blocking_threads.Count > 0) {
- if (abort_attempts++ >= ABORT_RETRIES) {
- if (THROW_ON_ABORT_RETRIES)
- throw new Exception ("Could not abort registered blocking threads before closing socket.");
-
- // Attempts to close the socket safely failed.
- // We give up, and close the socket with pending blocking system calls.
- // This should not occur, nonetheless if it does this avoids an endless loop.
- break;
- }
-
- /*
- * This method can be called by the DangerousRelease inside RegisterForBlockingSyscall
- * When this happens blocking_threads contains the current thread.
- * We can safely close the socket and throw SocketException in RegisterForBlockingSyscall
- * before the blocking system call.
- */
- lock (blocking_threads) {
+ lock (blocking_threads) {
+ int abort_attempts = 0;
+ while (blocking_threads.Count > 0) {
+ if (abort_attempts++ >= ABORT_RETRIES) {
+ if (THROW_ON_ABORT_RETRIES)
+ throw new Exception ("Could not abort registered blocking threads before closing socket.");
+
+ // Attempts to close the socket safely failed.
+ // We give up, and close the socket with pending blocking system calls.
+ // This should not occur, nonetheless if it does this avoids an endless loop.
+ break;
+ }
+
+ /*
+ * This method can be called by the DangerousRelease inside RegisterForBlockingSyscall
+ * When this happens blocking_threads contains the current thread.
+ * We can safely close the socket and throw SocketException in RegisterForBlockingSyscall
+ * before the blocking system call.
+ */
if (blocking_threads.Count == 1 && blocking_threads[0] == Thread.CurrentThread)
break;
- }
- AbortRegisteredThreads ();
- // Sleep so other threads can resume
- Thread.Sleep (1);
+ // abort registered threads
+ foreach (var t in blocking_threads)
+ Socket.cancel_blocking_socket_operation (t);
+
+ // Sleep so other threads can resume
+ in_cleanup = true;
+ Monitor.Wait (blocking_threads, 100);
+ }
}
}
//If this NRE, we're in deep problems because Register Must have
lock (blocking_threads) {
blocking_threads.Remove (Thread.CurrentThread);
- }
- }
-
- void AbortRegisteredThreads () {
- if (blocking_threads == null)
- return;
-
- lock (blocking_threads) {
- foreach (var t in blocking_threads)
- Socket.cancel_blocking_socket_operation (t);
+ if (in_cleanup && blocking_threads.Count == 0)
+ Monitor.Pulse (blocking_threads);
}
}
}
}
public bool IsLocal {
- get { return IPAddress.IsLoopback (RemoteEndPoint.Address); }
+ get { return LocalEndPoint.Address.Equals (RemoteEndPoint.Address); }
}
public bool IsSecureConnection {
X500DistinguishedNameFlags.UseT61Encoding | X500DistinguishedNameFlags.ForceUTF8Encoding;
private string name;
+ private byte[] canonEncoding;
public X500DistinguishedName (AsnEncodedData encodedDistinguishedName)
name = distinguishedName.name;
}
+ internal X500DistinguishedName (byte[] encoded, byte[] canonEncoding, string name)
+ : this (encoded)
+ {
+ this.canonEncoding = canonEncoding;
+ this.name = name;
+
+ Oid = new Oid ();
+ RawData = encoded;
+ }
+
+ internal byte[] CanonicalEncoding {
+ get { return canonEncoding; }
+ }
+
public string Name {
get { return name; }
if (name2 == null)
return false;
+ if (name1.canonEncoding != null && name2.canonEncoding != null) {
+ if (name1.canonEncoding.Length != name2.canonEncoding.Length)
+ return false;
+ for (int i = 0; i < name1.canonEncoding.Length; i++) {
+ if (name1.canonEncoding[i] != name2.canonEncoding[2])
+ return false;
+ }
+ return true;
+ }
+
X500DistinguishedNameFlags flags = X500DistinguishedNameFlags.UseNewLines | X500DistinguishedNameFlags.DoNotUseQuotes;
string[] split = new string[] { Environment.NewLine };
string[] parts1 = name1.Decode (flags).Split (split, StringSplitOptions.RemoveEmptyEntries);
using System.IO;
using System.Text;
+using System.Collections;
namespace System.Security.Cryptography.X509Certificates {
[Serializable]
public class X509Certificate2 : X509Certificate {
+
#if !SECURITY_DEP
// Used in Mono.Security HttpsClientStream
public X509Certificate2 (byte[] rawData)
}
#endif
#if SECURITY_DEP
- private bool _archived;
- private X509ExtensionCollection _extensions;
- private string _name = String.Empty;
- private string _serial;
- private PublicKey _publicKey;
- private X500DistinguishedName issuer_name;
- private X500DistinguishedName subject_name;
- private Oid signature_algorithm;
-
- private MX.X509Certificate _cert;
+ new internal X509Certificate2Impl Impl {
+ get {
+ var impl2 = base.Impl as X509Certificate2Impl;
+ X509Helper2.ThrowIfContextInvalid (impl2);
+ return impl2;
+ }
+ }
- private static string empty_error = Locale.GetText ("Certificate instance is empty.");
+ string friendlyName = string.Empty;
// constructors
public X509Certificate2 ()
{
- _cert = null;
}
public X509Certificate2 (byte[] rawData)
public X509Certificate2 (IntPtr handle) : base (handle)
{
- _cert = new MX.X509Certificate (base.GetRawCertData ());
+ throw new NotImplementedException ();
}
public X509Certificate2 (X509Certificate certificate)
- : base (certificate)
+ : base (X509Helper2.Import (certificate))
+ {
+ }
+
+ internal X509Certificate2 (X509Certificate2Impl impl)
+ : base (impl)
{
- _cert = new MX.X509Certificate (base.GetRawCertData ());
}
// properties
public bool Archived {
- get {
- if (_cert == null)
- throw new CryptographicException (empty_error);
- return _archived;
- }
- set {
- if (_cert == null)
- throw new CryptographicException (empty_error);
- _archived = value;
- }
+ get { return Impl.Archived; }
+ set { Impl.Archived = true; }
}
public X509ExtensionCollection Extensions {
- get {
- if (_cert == null)
- throw new CryptographicException (empty_error);
- if (_extensions == null)
- _extensions = new X509ExtensionCollection (_cert);
- return _extensions;
- }
+ get { return Impl.Extensions; }
}
public string FriendlyName {
get {
- if (_cert == null)
- throw new CryptographicException (empty_error);
- return _name;
+ ThrowIfContextInvalid ();
+ return friendlyName;
}
set {
- if (_cert == null)
- throw new CryptographicException (empty_error);
- _name = value;
+ ThrowIfContextInvalid ();
+ friendlyName = value;
}
}
- // FIXME - Could be more efficient
public bool HasPrivateKey {
- get { return PrivateKey != null; }
+ get { return Impl.HasPrivateKey; }
}
public X500DistinguishedName IssuerName {
- get {
- if (_cert == null)
- throw new CryptographicException (empty_error);
- if (issuer_name == null)
- issuer_name = new X500DistinguishedName (_cert.GetIssuerName ().GetBytes ());
- return issuer_name;
- }
+ get { return Impl.IssuerName; }
}
public DateTime NotAfter {
- get {
- if (_cert == null)
- throw new CryptographicException (empty_error);
- return _cert.ValidUntil.ToLocalTime ();
- }
+ get { return Impl.GetValidUntil ().ToLocalTime (); }
}
public DateTime NotBefore {
- get {
- if (_cert == null)
- throw new CryptographicException (empty_error);
- return _cert.ValidFrom.ToLocalTime ();
- }
+ get { return Impl.GetValidFrom ().ToLocalTime (); }
}
public AsymmetricAlgorithm PrivateKey {
- get {
- if (_cert == null)
- throw new CryptographicException (empty_error);
- try {
- if (_cert.RSA != null) {
- RSACryptoServiceProvider rcsp = _cert.RSA as RSACryptoServiceProvider;
- if (rcsp != null)
- return rcsp.PublicOnly ? null : rcsp;
-
- RSAManaged rsam = _cert.RSA as RSAManaged;
- if (rsam != null)
- return rsam.PublicOnly ? null : rsam;
-
- _cert.RSA.ExportParameters (true);
- return _cert.RSA;
- } else if (_cert.DSA != null) {
- DSACryptoServiceProvider dcsp = _cert.DSA as DSACryptoServiceProvider;
- if (dcsp != null)
- return dcsp.PublicOnly ? null : dcsp;
-
- _cert.DSA.ExportParameters (true);
- return _cert.DSA;
- }
- }
- catch {
- }
- return null;
- }
- set {
- if (_cert == null)
- throw new CryptographicException (empty_error);
-
- // allow NULL so we can "forget" the key associated to the certificate
- // e.g. in case we want to export it in another format (see bug #396620)
- if (value == null) {
- _cert.RSA = null;
- _cert.DSA = null;
- } else if (value is RSA)
- _cert.RSA = (RSA) value;
- else if (value is DSA)
- _cert.DSA = (DSA) value;
- else
- throw new NotSupportedException ();
- }
+ get { return Impl.PrivateKey; }
+ set { Impl.PrivateKey = value; }
}
public PublicKey PublicKey {
- get {
- if (_cert == null)
- throw new CryptographicException (empty_error);
-
- if (_publicKey == null) {
- try {
- _publicKey = new PublicKey (_cert);
- }
- catch (Exception e) {
- string msg = Locale.GetText ("Unable to decode public key.");
- throw new CryptographicException (msg, e);
- }
- }
- return _publicKey;
- }
+ get { return Impl.PublicKey; }
}
public byte[] RawData {
- get {
- if (_cert == null)
- throw new CryptographicException (empty_error);
-
- return base.GetRawCertData ();
- }
- }
+ get { return GetRawCertData (); }
+ }
public string SerialNumber {
- get {
- if (_cert == null)
- throw new CryptographicException (empty_error);
-
- if (_serial == null) {
- StringBuilder sb = new StringBuilder ();
- byte[] serial = _cert.SerialNumber;
- for (int i=serial.Length - 1; i >= 0; i--)
- sb.Append (serial [i].ToString ("X2"));
- _serial = sb.ToString ();
- }
- return _serial;
- }
+ get { return GetSerialNumberString (); }
}
public Oid SignatureAlgorithm {
- get {
- if (_cert == null)
- throw new CryptographicException (empty_error);
-
- if (signature_algorithm == null)
- signature_algorithm = new Oid (_cert.SignatureAlgorithm);
- return signature_algorithm;
- }
+ get { return Impl.SignatureAlgorithm; }
}
public X500DistinguishedName SubjectName {
- get {
- if (_cert == null)
- throw new CryptographicException (empty_error);
-
- if (subject_name == null)
- subject_name = new X500DistinguishedName (_cert.GetSubjectName ().GetBytes ());
- return subject_name;
- }
+ get { return Impl.SubjectName; }
}
public string Thumbprint {
- get { return base.GetCertHashString (); }
+ get { return GetCertHashString (); }
}
public int Version {
- get {
- if (_cert == null)
- throw new CryptographicException (empty_error);
- return _cert.Version;
- }
+ get { return Impl.Version; }
}
// methods
[MonoTODO ("always return String.Empty for UpnName, DnsFromAlternativeName and UrlName")]
public string GetNameInfo (X509NameType nameType, bool forIssuer)
{
- switch (nameType) {
- case X509NameType.SimpleName:
- if (_cert == null)
- throw new CryptographicException (empty_error);
- // return CN= or, if missing, the first part of the DN
- ASN1 sn = forIssuer ? _cert.GetIssuerName () : _cert.GetSubjectName ();
- ASN1 dn = Find (commonName, sn);
- if (dn != null)
- return GetValueAsString (dn);
- if (sn.Count == 0)
- return String.Empty;
- ASN1 last_entry = sn [sn.Count - 1];
- if (last_entry.Count == 0)
- return String.Empty;
- return GetValueAsString (last_entry [0]);
- case X509NameType.EmailName:
- // return the E= part of the DN (if present)
- ASN1 e = Find (email, forIssuer ? _cert.GetIssuerName () : _cert.GetSubjectName ());
- if (e != null)
- return GetValueAsString (e);
- return String.Empty;
- case X509NameType.UpnName:
- // FIXME - must find/create test case
- return String.Empty;
- case X509NameType.DnsName:
- // return the CN= part of the DN (if present)
- ASN1 cn = Find (commonName, forIssuer ? _cert.GetIssuerName () : _cert.GetSubjectName ());
- if (cn != null)
- return GetValueAsString (cn);
- return String.Empty;
- case X509NameType.DnsFromAlternativeName:
- // FIXME - must find/create test case
- return String.Empty;
- case X509NameType.UrlName:
- // FIXME - must find/create test case
- return String.Empty;
- default:
- throw new ArgumentException ("nameType");
- }
- }
-
- static byte[] commonName = { 0x55, 0x04, 0x03 };
- static byte[] email = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01 };
-
- private ASN1 Find (byte[] oid, ASN1 dn)
- {
- if (dn.Count == 0)
- return null;
-
- // process SET
- for (int i = 0; i < dn.Count; i++) {
- ASN1 set = dn [i];
- for (int j = 0; j < set.Count; j++) {
- ASN1 pair = set [j];
- if (pair.Count != 2)
- continue;
-
- ASN1 poid = pair [0];
- if (poid == null)
- continue;
-
- if (poid.CompareValue (oid))
- return pair;
- }
- }
- return null;
- }
-
- private string GetValueAsString (ASN1 pair)
- {
- if (pair.Count != 2)
- return String.Empty;
-
- ASN1 value = pair [1];
- if ((value.Value == null) || (value.Length == 0))
- return String.Empty;
-
- if (value.Tag == 0x1E) {
- // BMPSTRING
- StringBuilder sb = new StringBuilder ();
- for (int j = 1; j < value.Value.Length; j += 2)
- sb.Append ((char)value.Value [j]);
- return sb.ToString ();
- } else {
- return Encoding.UTF8.GetString (value.Value);
- }
- }
-
- private MX.X509Certificate ImportPkcs12 (byte[] rawData, string password)
- {
- MX.PKCS12 pfx = null;
- if (string.IsNullOrEmpty (password)) {
- try {
- // Support both unencrypted PKCS#12..
- pfx = new MX.PKCS12 (rawData, (string)null);
- } catch {
- // ..and PKCS#12 encrypted with an empty password
- pfx = new MX.PKCS12 (rawData, string.Empty);
- }
- } else {
- pfx = new MX.PKCS12 (rawData, password);
- }
-
- if (pfx.Certificates.Count == 0) {
- // no certificate was found
- return null;
- } else if (pfx.Keys.Count == 0) {
- // no key were found - pick the first certificate
- return pfx.Certificates [0];
- } else {
- // find the certificate that match the first key
- MX.X509Certificate cert = null;
- var keypair = (pfx.Keys [0] as AsymmetricAlgorithm);
- string pubkey = keypair.ToXmlString (false);
- foreach (var c in pfx.Certificates) {
- if (((c.RSA != null) && (pubkey == c.RSA.ToXmlString (false))) ||
- ((c.DSA != null) && (pubkey == c.DSA.ToXmlString (false)))) {
- cert = c;
- break;
- }
- }
- if (cert == null) {
- cert = pfx.Certificates [0]; // no match, pick first certificate without keys
- } else {
- cert.RSA = (keypair as RSA);
- cert.DSA = (keypair as DSA);
- }
- return cert;
- }
+ return Impl.GetNameInfo (nameType, forIssuer);
}
public override void Import (byte[] rawData)
[MonoTODO ("missing KeyStorageFlags support")]
public override void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
{
- MX.X509Certificate cert = null;
- if (password == null) {
- try {
- cert = new MX.X509Certificate (rawData);
- }
- catch (Exception e) {
- try {
- cert = ImportPkcs12 (rawData, null);
- }
- catch {
- string msg = Locale.GetText ("Unable to decode certificate.");
- // inner exception is the original (not second) exception
- throw new CryptographicException (msg, e);
- }
- }
- } else {
- // try PKCS#12
- try {
- cert = ImportPkcs12 (rawData, password);
- }
- catch {
- // it's possible to supply a (unrequired/unusued) password
- // fix bug #79028
- cert = new MX.X509Certificate (rawData);
- }
- }
- // we do not have to fully re-decode the certificate since X509Certificate does not deal with keys
- if (cert != null) {
- base.Import (cert.RawData, (string) null, keyStorageFlags);
- _cert = cert; // becuase base call will call Reset!
- }
+ var impl = X509Helper2.Import (rawData, password, keyStorageFlags);
+ ImportHandle (impl);
}
[MonoTODO ("SecureString is incomplete")]
[MonoTODO ("X509ContentType.SerializedCert is not supported")]
public override byte[] Export (X509ContentType contentType, string password)
{
- if (_cert == null)
- throw new CryptographicException (empty_error);
-
- switch (contentType) {
- case X509ContentType.Cert:
- return _cert.RawData;
- case X509ContentType.Pfx: // this includes Pkcs12
- return ExportPkcs12 (password);
- case X509ContentType.SerializedCert:
- // TODO
- throw new NotSupportedException ();
- default:
- string msg = Locale.GetText ("This certificate format '{0}' cannot be exported.", contentType);
- throw new CryptographicException (msg);
- }
- }
-
- byte[] ExportPkcs12 (string password)
- {
- var pfx = new MX.PKCS12 ();
- try {
- if (password != null)
- pfx.Password = password;
- pfx.AddCertificate (_cert);
- var privateKey = PrivateKey;
- if (privateKey != null)
- pfx.AddPkcs8ShroudedKeyBag (privateKey);
- return pfx.GetBytes ();
- } finally {
- pfx.Password = null;
- }
+ return Impl.Export (contentType, password);
}
public override void Reset ()
{
- _cert = null;
- _archived = false;
- _extensions = null;
- _name = String.Empty;
- _serial = null;
- _publicKey = null;
- issuer_name = null;
- subject_name = null;
- signature_algorithm = null;
+ friendlyName = string.Empty;
base.Reset ();
}
public override string ToString ()
{
- if (_cert == null)
+ if (!IsValid)
return "System.Security.Cryptography.X509Certificates.X509Certificate2";
-
return base.ToString (true);
}
public override string ToString (bool verbose)
{
- if (_cert == null)
+ if (!IsValid)
return "System.Security.Cryptography.X509Certificates.X509Certificate2";
// the non-verbose X509Certificate2 == verbose X509Certificate
[MonoTODO ("by default this depends on the incomplete X509Chain")]
public bool Verify ()
{
- if (_cert == null)
- throw new CryptographicException (empty_error);
-
- X509Chain chain = X509Chain.Create ();
- if (!chain.Build (this))
- return false;
- // TODO - check chain and other stuff ???
- return true;
+ return Impl.Verify (this);
}
// static methods
// internal stuff because X509Certificate2 isn't complete enough
// (maybe X509Certificate3 will be better?)
+ [Obsolete ("KILL")]
internal MX.X509Certificate MonoCertificate {
- get { return _cert; }
+ get {
+ var monoImpl = Impl as X509Certificate2ImplMono;
+ if (monoImpl == null)
+ throw new NotSupportedException ();
+ return monoImpl.MonoCertificate;
+ }
}
#else
--- /dev/null
+//
+// X509Certificate2Impl.cs
+//
+// Authors:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (C) 2016 Xamarin, Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace System.Security.Cryptography.X509Certificates
+{
+ internal abstract class X509Certificate2Impl : X509CertificateImpl
+ {
+#if SECURITY_DEP
+
+ public abstract bool Archived {
+ get; set;
+ }
+
+ public abstract X509ExtensionCollection Extensions {
+ get;
+ }
+
+ public abstract bool HasPrivateKey {
+ get;
+ }
+
+ public abstract X500DistinguishedName IssuerName {
+ get;
+ }
+
+ public abstract AsymmetricAlgorithm PrivateKey {
+ get; set;
+ }
+
+ public abstract PublicKey PublicKey {
+ get;
+ }
+
+ public abstract Oid SignatureAlgorithm {
+ get;
+ }
+
+ public abstract X500DistinguishedName SubjectName {
+ get;
+ }
+
+ public abstract int Version {
+ get;
+ }
+
+ public abstract string GetNameInfo (X509NameType nameType, bool forIssuer);
+
+ public abstract void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags);
+
+ public abstract byte[] Export (X509ContentType contentType, string password);
+
+ public abstract bool Verify (X509Certificate2 thisCertificate);
+
+ public abstract void Reset ();
+
+#endif
+ }
+}
--- /dev/null
+//
+// X509Certificate2ImplMono
+//
+// Authors:
+// Sebastien Pouliot <sebastien@xamarin.com>
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
+// Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com)
+// Copyright (C) 2015-2016 Xamarin, Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if SECURITY_DEP
+
+#if MONO_SECURITY_ALIAS
+extern alias MonoSecurity;
+using MonoSecurity::Mono.Security;
+using MonoSecurity::Mono.Security.Cryptography;
+using MX = MonoSecurity::Mono.Security.X509;
+#else
+using Mono.Security;
+using Mono.Security.Cryptography;
+using MX = Mono.Security.X509;
+#endif
+
+using System.IO;
+using System.Text;
+using System.Collections;
+
+namespace System.Security.Cryptography.X509Certificates
+{
+ internal class X509Certificate2ImplMono : X509Certificate2Impl
+ {
+ bool _archived;
+ X509ExtensionCollection _extensions;
+ string _serial;
+ PublicKey _publicKey;
+ X500DistinguishedName issuer_name;
+ X500DistinguishedName subject_name;
+ Oid signature_algorithm;
+
+ MX.X509Certificate _cert;
+
+ static string empty_error = Locale.GetText ("Certificate instance is empty.");
+
+ public override bool IsValid {
+ get {
+ return _cert != null;
+ }
+ }
+
+ public override IntPtr Handle {
+ get { return IntPtr.Zero; }
+ }
+
+ internal X509Certificate2ImplMono (MX.X509Certificate cert)
+ {
+ this._cert = cert;
+ }
+
+ public override X509CertificateImpl Clone ()
+ {
+ ThrowIfContextInvalid ();
+ return new X509Certificate2ImplMono (_cert);
+ }
+
+ #region Implemented X509CertificateImpl members
+
+ public override string GetIssuerName (bool legacyV1Mode)
+ {
+ ThrowIfContextInvalid ();
+ if (legacyV1Mode)
+ return _cert.IssuerName;
+ else
+ return MX.X501.ToString (_cert.GetIssuerName (), true, ", ", true);
+ }
+
+ public override string GetSubjectName (bool legacyV1Mode)
+ {
+ ThrowIfContextInvalid ();
+ if (legacyV1Mode)
+ return _cert.SubjectName;
+ else
+ return MX.X501.ToString (_cert.GetSubjectName (), true, ", ", true);
+ }
+
+ public override byte[] GetRawCertData ()
+ {
+ ThrowIfContextInvalid ();
+ return _cert.RawData;
+ }
+
+ protected override byte[] GetCertHash (bool lazy)
+ {
+ ThrowIfContextInvalid ();
+ SHA1 sha = SHA1.Create ();
+ return sha.ComputeHash (_cert.RawData);
+ }
+
+ public override DateTime GetValidFrom ()
+ {
+ ThrowIfContextInvalid ();
+ return _cert.ValidFrom;
+ }
+
+ public override DateTime GetValidUntil ()
+ {
+ ThrowIfContextInvalid ();
+ return _cert.ValidUntil;
+ }
+
+ public override bool Equals (X509CertificateImpl other, out bool result)
+ {
+ // Use default implementation
+ result = false;
+ return false;
+ }
+
+ public override string GetKeyAlgorithm ()
+ {
+ ThrowIfContextInvalid ();
+ return _cert.KeyAlgorithm;
+ }
+
+ public override byte[] GetKeyAlgorithmParameters ()
+ {
+ ThrowIfContextInvalid ();
+ return _cert.KeyAlgorithmParameters;
+ }
+
+ public override byte[] GetPublicKey ()
+ {
+ ThrowIfContextInvalid ();
+ return _cert.PublicKey;
+ }
+
+ public override byte[] GetSerialNumber ()
+ {
+ ThrowIfContextInvalid ();
+ return _cert.SerialNumber;
+ }
+
+ public override byte[] Export (X509ContentType contentType, byte[] password)
+ {
+ ThrowIfContextInvalid ();
+
+ switch (contentType) {
+ case X509ContentType.Cert:
+ return GetRawCertData ();
+ case X509ContentType.Pfx: // this includes Pkcs12
+ // TODO
+ throw new NotSupportedException ();
+ case X509ContentType.SerializedCert:
+ // TODO
+ throw new NotSupportedException ();
+ default:
+ string msg = Locale.GetText ("This certificate format '{0}' cannot be exported.", contentType);
+ throw new CryptographicException (msg);
+ }
+ }
+
+ #endregion
+
+ // constructors
+
+ public X509Certificate2ImplMono ()
+ {
+ _cert = null;
+ }
+
+ // properties
+
+ public override bool Archived {
+ get {
+ if (_cert == null)
+ throw new CryptographicException (empty_error);
+ return _archived;
+ }
+ set {
+ if (_cert == null)
+ throw new CryptographicException (empty_error);
+ _archived = value;
+ }
+ }
+
+ public override X509ExtensionCollection Extensions {
+ get {
+ if (_cert == null)
+ throw new CryptographicException (empty_error);
+ if (_extensions == null)
+ _extensions = new X509ExtensionCollection (_cert);
+ return _extensions;
+ }
+ }
+
+ // FIXME - Could be more efficient
+ public override bool HasPrivateKey {
+ get { return PrivateKey != null; }
+ }
+
+ public override X500DistinguishedName IssuerName {
+ get {
+ if (_cert == null)
+ throw new CryptographicException (empty_error);
+ if (issuer_name == null)
+ issuer_name = new X500DistinguishedName (_cert.GetIssuerName ().GetBytes ());
+ return issuer_name;
+ }
+ }
+
+ public override AsymmetricAlgorithm PrivateKey {
+ get {
+ if (_cert == null)
+ throw new CryptographicException (empty_error);
+ try {
+ if (_cert.RSA != null) {
+ RSACryptoServiceProvider rcsp = _cert.RSA as RSACryptoServiceProvider;
+ if (rcsp != null)
+ return rcsp.PublicOnly ? null : rcsp;
+
+ RSAManaged rsam = _cert.RSA as RSAManaged;
+ if (rsam != null)
+ return rsam.PublicOnly ? null : rsam;
+
+ _cert.RSA.ExportParameters (true);
+ return _cert.RSA;
+ } else if (_cert.DSA != null) {
+ DSACryptoServiceProvider dcsp = _cert.DSA as DSACryptoServiceProvider;
+ if (dcsp != null)
+ return dcsp.PublicOnly ? null : dcsp;
+
+ _cert.DSA.ExportParameters (true);
+ return _cert.DSA;
+ }
+ }
+ catch {
+ }
+ return null;
+ }
+ set {
+ if (_cert == null)
+ throw new CryptographicException (empty_error);
+
+ // allow NULL so we can "forget" the key associated to the certificate
+ // e.g. in case we want to export it in another format (see bug #396620)
+ if (value == null) {
+ _cert.RSA = null;
+ _cert.DSA = null;
+ } else if (value is RSA)
+ _cert.RSA = (RSA) value;
+ else if (value is DSA)
+ _cert.DSA = (DSA) value;
+ else
+ throw new NotSupportedException ();
+ }
+ }
+
+ public override PublicKey PublicKey {
+ get {
+ if (_cert == null)
+ throw new CryptographicException (empty_error);
+
+ if (_publicKey == null) {
+ try {
+ _publicKey = new PublicKey (_cert);
+ }
+ catch (Exception e) {
+ string msg = Locale.GetText ("Unable to decode public key.");
+ throw new CryptographicException (msg, e);
+ }
+ }
+ return _publicKey;
+ }
+ }
+
+ public override Oid SignatureAlgorithm {
+ get {
+ if (_cert == null)
+ throw new CryptographicException (empty_error);
+
+ if (signature_algorithm == null)
+ signature_algorithm = new Oid (_cert.SignatureAlgorithm);
+ return signature_algorithm;
+ }
+ }
+
+ public override X500DistinguishedName SubjectName {
+ get {
+ if (_cert == null)
+ throw new CryptographicException (empty_error);
+
+ if (subject_name == null)
+ subject_name = new X500DistinguishedName (_cert.GetSubjectName ().GetBytes ());
+ return subject_name;
+ }
+ }
+
+ public override int Version {
+ get {
+ if (_cert == null)
+ throw new CryptographicException (empty_error);
+ return _cert.Version;
+ }
+ }
+
+ // methods
+
+ [MonoTODO ("always return String.Empty for UpnName, DnsFromAlternativeName and UrlName")]
+ public override string GetNameInfo (X509NameType nameType, bool forIssuer)
+ {
+ switch (nameType) {
+ case X509NameType.SimpleName:
+ if (_cert == null)
+ throw new CryptographicException (empty_error);
+ // return CN= or, if missing, the first part of the DN
+ ASN1 sn = forIssuer ? _cert.GetIssuerName () : _cert.GetSubjectName ();
+ ASN1 dn = Find (commonName, sn);
+ if (dn != null)
+ return GetValueAsString (dn);
+ if (sn.Count == 0)
+ return String.Empty;
+ ASN1 last_entry = sn [sn.Count - 1];
+ if (last_entry.Count == 0)
+ return String.Empty;
+ return GetValueAsString (last_entry [0]);
+ case X509NameType.EmailName:
+ // return the E= part of the DN (if present)
+ ASN1 e = Find (email, forIssuer ? _cert.GetIssuerName () : _cert.GetSubjectName ());
+ if (e != null)
+ return GetValueAsString (e);
+ return String.Empty;
+ case X509NameType.UpnName:
+ // FIXME - must find/create test case
+ return String.Empty;
+ case X509NameType.DnsName:
+ // return the CN= part of the DN (if present)
+ ASN1 cn = Find (commonName, forIssuer ? _cert.GetIssuerName () : _cert.GetSubjectName ());
+ if (cn != null)
+ return GetValueAsString (cn);
+ return String.Empty;
+ case X509NameType.DnsFromAlternativeName:
+ // FIXME - must find/create test case
+ return String.Empty;
+ case X509NameType.UrlName:
+ // FIXME - must find/create test case
+ return String.Empty;
+ default:
+ throw new ArgumentException ("nameType");
+ }
+ }
+
+ static byte[] commonName = { 0x55, 0x04, 0x03 };
+ static byte[] email = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01 };
+
+ private ASN1 Find (byte[] oid, ASN1 dn)
+ {
+ if (dn.Count == 0)
+ return null;
+
+ // process SET
+ for (int i = 0; i < dn.Count; i++) {
+ ASN1 set = dn [i];
+ for (int j = 0; j < set.Count; j++) {
+ ASN1 pair = set [j];
+ if (pair.Count != 2)
+ continue;
+
+ ASN1 poid = pair [0];
+ if (poid == null)
+ continue;
+
+ if (poid.CompareValue (oid))
+ return pair;
+ }
+ }
+ return null;
+ }
+
+ private string GetValueAsString (ASN1 pair)
+ {
+ if (pair.Count != 2)
+ return String.Empty;
+
+ ASN1 value = pair [1];
+ if ((value.Value == null) || (value.Length == 0))
+ return String.Empty;
+
+ if (value.Tag == 0x1E) {
+ // BMPSTRING
+ StringBuilder sb = new StringBuilder ();
+ for (int j = 1; j < value.Value.Length; j += 2)
+ sb.Append ((char)value.Value [j]);
+ return sb.ToString ();
+ } else {
+ return Encoding.UTF8.GetString (value.Value);
+ }
+ }
+
+ private MX.X509Certificate ImportPkcs12 (byte[] rawData, string password)
+ {
+ MX.PKCS12 pfx = null;
+ if (string.IsNullOrEmpty (password)) {
+ try {
+ // Support both unencrypted PKCS#12..
+ pfx = new MX.PKCS12 (rawData, (string)null);
+ } catch {
+ // ..and PKCS#12 encrypted with an empty password
+ pfx = new MX.PKCS12 (rawData, string.Empty);
+ }
+ } else {
+ pfx = new MX.PKCS12 (rawData, password);
+ }
+
+ if (pfx.Certificates.Count == 0) {
+ // no certificate was found
+ return null;
+ } else if (pfx.Keys.Count == 0) {
+ // no key were found - pick the first certificate
+ return pfx.Certificates [0];
+ } else {
+ // find the certificate that match the first key
+ MX.X509Certificate cert = null;
+ var keypair = (pfx.Keys [0] as AsymmetricAlgorithm);
+ string pubkey = keypair.ToXmlString (false);
+ foreach (var c in pfx.Certificates) {
+ if (((c.RSA != null) && (pubkey == c.RSA.ToXmlString (false))) ||
+ ((c.DSA != null) && (pubkey == c.DSA.ToXmlString (false)))) {
+ cert = c;
+ break;
+ }
+ }
+ if (cert == null) {
+ cert = pfx.Certificates [0]; // no match, pick first certificate without keys
+ } else {
+ cert.RSA = (keypair as RSA);
+ cert.DSA = (keypair as DSA);
+ }
+ return cert;
+ }
+ }
+
+ [MonoTODO ("missing KeyStorageFlags support")]
+ public override void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
+ {
+ MX.X509Certificate cert = null;
+ if (password == null) {
+ try {
+ cert = new MX.X509Certificate (rawData);
+ }
+ catch (Exception e) {
+ try {
+ cert = ImportPkcs12 (rawData, null);
+ }
+ catch {
+ string msg = Locale.GetText ("Unable to decode certificate.");
+ // inner exception is the original (not second) exception
+ throw new CryptographicException (msg, e);
+ }
+ }
+ } else {
+ // try PKCS#12
+ try {
+ cert = ImportPkcs12 (rawData, password);
+ }
+ catch {
+ // it's possible to supply a (unrequired/unusued) password
+ // fix bug #79028
+ cert = new MX.X509Certificate (rawData);
+ }
+ }
+ _cert = cert;
+ }
+
+ [MonoTODO ("X509ContentType.SerializedCert is not supported")]
+ public override byte[] Export (X509ContentType contentType, string password)
+ {
+ if (_cert == null)
+ throw new CryptographicException (empty_error);
+
+ switch (contentType) {
+ case X509ContentType.Cert:
+ return _cert.RawData;
+ case X509ContentType.Pfx: // this includes Pkcs12
+ return ExportPkcs12 (password);
+ case X509ContentType.SerializedCert:
+ // TODO
+ throw new NotSupportedException ();
+ default:
+ string msg = Locale.GetText ("This certificate format '{0}' cannot be exported.", contentType);
+ throw new CryptographicException (msg);
+ }
+ }
+
+ byte[] ExportPkcs12 (string password)
+ {
+ var pfx = new MX.PKCS12 ();
+ try {
+ var attrs = new Hashtable ();
+ var localKeyId = new ArrayList ();
+ localKeyId.Add (new byte[] { 1, 0, 0, 0 });
+ attrs.Add (MX.PKCS9.localKeyId, localKeyId);
+
+ if (password != null)
+ pfx.Password = password;
+ pfx.AddCertificate (_cert, attrs);
+ var privateKey = PrivateKey;
+ if (privateKey != null)
+ pfx.AddPkcs8ShroudedKeyBag (privateKey, attrs);
+ return pfx.GetBytes ();
+ } finally {
+ pfx.Password = null;
+ }
+ }
+
+ public override void Reset ()
+ {
+ _cert = null;
+ _archived = false;
+ _extensions = null;
+ _serial = null;
+ _publicKey = null;
+ issuer_name = null;
+ subject_name = null;
+ signature_algorithm = null;
+ }
+
+ public override string ToString ()
+ {
+ if (_cert == null)
+ return "System.Security.Cryptography.X509Certificates.X509Certificate2";
+
+ return ToString (true);
+ }
+
+ public override string ToString (bool verbose)
+ {
+ if (_cert == null)
+ return "System.Security.Cryptography.X509Certificates.X509Certificate2";
+
+ string nl = Environment.NewLine;
+ StringBuilder sb = new StringBuilder ();
+
+ // the non-verbose X509Certificate2 == verbose X509Certificate
+ if (!verbose) {
+ sb.AppendFormat ("[Subject]{0} {1}{0}{0}", nl, GetSubjectName (false));
+ sb.AppendFormat ("[Issuer]{0} {1}{0}{0}", nl, GetIssuerName (false));
+ sb.AppendFormat ("[Not Before]{0} {1}{0}{0}", nl, GetValidFrom ().ToLocalTime ());
+ sb.AppendFormat ("[Not After]{0} {1}{0}{0}", nl, GetValidUntil ().ToLocalTime ());
+ sb.AppendFormat ("[Thumbprint]{0} {1}{0}", nl, X509Helper.ToHexString (GetCertHash ()));
+ sb.Append (nl);
+ return sb.ToString ();
+ }
+
+ sb.AppendFormat ("[Version]{0} V{1}{0}{0}", nl, Version);
+ sb.AppendFormat ("[Subject]{0} {1}{0}{0}", nl, GetSubjectName (false));
+ sb.AppendFormat ("[Issuer]{0} {1}{0}{0}", nl, GetIssuerName (false));
+ sb.AppendFormat ("[Serial Number]{0} {1}{0}{0}", nl, GetSerialNumber ());
+ sb.AppendFormat ("[Not Before]{0} {1}{0}{0}", nl, GetValidFrom ().ToLocalTime ());
+ sb.AppendFormat ("[Not After]{0} {1}{0}{0}", nl, GetValidUntil ().ToLocalTime ());
+ sb.AppendFormat ("[Thumbprint]{0} {1}{0}", nl, X509Helper.ToHexString (GetCertHash ()));
+ sb.AppendFormat ("[Signature Algorithm]{0} {1}({2}){0}{0}", nl, SignatureAlgorithm.FriendlyName,
+ SignatureAlgorithm.Value);
+
+ AsymmetricAlgorithm key = PublicKey.Key;
+ sb.AppendFormat ("[Public Key]{0} Algorithm: ", nl);
+ if (key is RSA)
+ sb.Append ("RSA");
+ else if (key is DSA)
+ sb.Append ("DSA");
+ else
+ sb.Append (key.ToString ());
+ sb.AppendFormat ("{0} Length: {1}{0} Key Blob: ", nl, key.KeySize);
+ AppendBuffer (sb, PublicKey.EncodedKeyValue.RawData);
+ sb.AppendFormat ("{0} Parameters: ", nl);
+ AppendBuffer (sb, PublicKey.EncodedParameters.RawData);
+ sb.Append (nl);
+
+ return sb.ToString ();
+ }
+
+ private static void AppendBuffer (StringBuilder sb, byte[] buffer)
+ {
+ if (buffer == null)
+ return;
+ for (int i=0; i < buffer.Length; i++) {
+ sb.Append (buffer [i].ToString ("x2"));
+ if (i < buffer.Length - 1)
+ sb.Append (" ");
+ }
+ }
+
+ [MonoTODO ("by default this depends on the incomplete X509Chain")]
+ public override bool Verify (X509Certificate2 thisCertificate)
+ {
+ if (_cert == null)
+ throw new CryptographicException (empty_error);
+
+ X509Chain chain = X509Chain.Create ();
+ if (!chain.Build (thisCertificate))
+ return false;
+ // TODO - check chain and other stuff ???
+ return true;
+ }
+
+ // static methods
+
+ private static byte[] signedData = new byte[] { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02 };
+
+ [MonoTODO ("Detection limited to Cert, Pfx, Pkcs12, Pkcs7 and Unknown")]
+ public static X509ContentType GetCertContentType (byte[] rawData)
+ {
+ if ((rawData == null) || (rawData.Length == 0))
+ throw new ArgumentException ("rawData");
+
+ X509ContentType type = X509ContentType.Unknown;
+ try {
+ ASN1 data = new ASN1 (rawData);
+ if (data.Tag != 0x30) {
+ string msg = Locale.GetText ("Unable to decode certificate.");
+ throw new CryptographicException (msg);
+ }
+
+ if (data.Count == 0)
+ return type;
+
+ if (data.Count == 3) {
+ switch (data [0].Tag) {
+ case 0x30:
+ // SEQUENCE / SEQUENCE / BITSTRING
+ if ((data [1].Tag == 0x30) && (data [2].Tag == 0x03))
+ type = X509ContentType.Cert;
+ break;
+ case 0x02:
+ // INTEGER / SEQUENCE / SEQUENCE
+ if ((data [1].Tag == 0x30) && (data [2].Tag == 0x30))
+ type = X509ContentType.Pkcs12;
+ // note: Pfx == Pkcs12
+ break;
+ }
+ }
+ // check for PKCS#7 (count unknown but greater than 0)
+ // SEQUENCE / OID (signedData)
+ if ((data [0].Tag == 0x06) && data [0].CompareValue (signedData))
+ type = X509ContentType.Pkcs7;
+ }
+ catch (Exception e) {
+ string msg = Locale.GetText ("Unable to decode certificate.");
+ throw new CryptographicException (msg, e);
+ }
+
+ return type;
+ }
+
+ [MonoTODO ("Detection limited to Cert, Pfx, Pkcs12 and Unknown")]
+ public static X509ContentType GetCertContentType (string fileName)
+ {
+ if (fileName == null)
+ throw new ArgumentNullException ("fileName");
+ if (fileName.Length == 0)
+ throw new ArgumentException ("fileName");
+
+ byte[] data = File.ReadAllBytes (fileName);
+ return GetCertContentType (data);
+ }
+
+ // internal stuff because X509Certificate2 isn't complete enough
+ // (maybe X509Certificate3 will be better?)
+
+ internal MX.X509Certificate MonoCertificate {
+ get { return _cert; }
+ }
+ }
+}
+
+#endif
namespace System.Security.Cryptography.X509Certificates {
- public class X509Chain {
+ public class X509Chain : IDisposable {
- private StoreLocation location;
- private X509ChainElementCollection elements;
- private X509ChainPolicy policy;
- private X509ChainStatus[] status;
+ X509ChainImpl impl;
static X509ChainStatus[] Empty = new X509ChainStatus [0];
- // RFC3280 variables
- private int max_path_length;
- private X500DistinguishedName working_issuer_name;
-// private string working_public_key_algorithm;
- private AsymmetricAlgorithm working_public_key;
+ internal X509ChainImpl Impl {
+ get {
+ X509Helper2.ThrowIfContextInvalid (impl);
+ return impl;
+ }
+ }
- // other flags
- private X509ChainElement bce_restriction;
+ internal bool IsValid {
+ get { return X509Helper2.IsValid (impl); }
+ }
+
+ internal void ThrowIfContextInvalid ()
+ {
+ X509Helper2.ThrowIfContextInvalid (impl);
+ }
// constructors
public X509Chain (bool useMachineContext)
{
- location = useMachineContext ? StoreLocation.LocalMachine : StoreLocation.CurrentUser;
- elements = new X509ChainElementCollection ();
- policy = new X509ChainPolicy ();
+ impl = X509Helper2.CreateChainImpl (useMachineContext);
+ }
+
+ internal X509Chain (X509ChainImpl impl)
+ {
+ X509Helper2.ThrowIfContextInvalid (impl);
+ this.impl = impl;
}
[MonoTODO ("Mono's X509Chain is fully managed. All handles are invalid.")]
[MonoTODO ("Mono's X509Chain is fully managed. Always returns IntPtr.Zero.")]
public IntPtr ChainContext {
- get { return IntPtr.Zero; }
+ get {
+ if (impl != null && impl.IsValid)
+ return impl.Handle;
+ return IntPtr.Zero;
+ }
}
public X509ChainElementCollection ChainElements {
- get { return elements; }
+ get { return Impl.ChainElements; }
}
public X509ChainPolicy ChainPolicy {
- get { return policy; }
- set { policy = value; }
+ get { return Impl.ChainPolicy; }
+ set { Impl.ChainPolicy = value; }
}
public X509ChainStatus[] ChainStatus {
- get {
- if (status == null)
- return Empty;
- return status;
- }
- }
+ get { return Impl.ChainStatus; }
+ }
// methods
[MonoTODO ("Not totally RFC3280 compliant, but neither is MS implementation...")]
public bool Build (X509Certificate2 certificate)
{
- if (certificate == null)
- throw new ArgumentException ("certificate");
-
- Reset ();
- X509ChainStatusFlags flag;
- try {
- flag = BuildChainFrom (certificate);
- ValidateChain (flag);
- }
- catch (CryptographicException ce) {
- throw new ArgumentException ("certificate", ce);
- }
-
- X509ChainStatusFlags total = X509ChainStatusFlags.NoError;
- ArrayList list = new ArrayList ();
- // build "global" ChainStatus from the ChainStatus of every ChainElements
- foreach (X509ChainElement ce in elements) {
- foreach (X509ChainStatus cs in ce.ChainElementStatus) {
- // we MUST avoid duplicates in the "global" list
- if ((total & cs.Status) != cs.Status) {
- list.Add (cs);
- total |= cs.Status;
- }
- }
- }
- // and if required add some
- if (flag != X509ChainStatusFlags.NoError) {
- list.Insert (0, new X509ChainStatus (flag));
- }
- status = (X509ChainStatus[]) list.ToArray (typeof (X509ChainStatus));
-
- // (fast path) this ignore everything we have checked
- if ((status.Length == 0) || (ChainPolicy.VerificationFlags == X509VerificationFlags.AllFlags))
- return true;
-
- bool result = true;
- // now check if exclude some verification for the "end result" (boolean)
- foreach (X509ChainStatus cs in status) {
- switch (cs.Status) {
- case X509ChainStatusFlags.UntrustedRoot:
- case X509ChainStatusFlags.PartialChain:
- result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.AllowUnknownCertificateAuthority) != 0);
- break;
- case X509ChainStatusFlags.NotTimeValid:
- result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreNotTimeValid) != 0);
- break;
- // FIXME - from here we needs new test cases for all cases
- case X509ChainStatusFlags.NotTimeNested:
- result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreNotTimeNested) != 0);
- break;
- case X509ChainStatusFlags.InvalidBasicConstraints:
- result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreInvalidBasicConstraints) != 0);
- break;
- case X509ChainStatusFlags.InvalidPolicyConstraints:
- case X509ChainStatusFlags.NoIssuanceChainPolicy:
- result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreInvalidPolicy) != 0);
- break;
- case X509ChainStatusFlags.InvalidNameConstraints:
- case X509ChainStatusFlags.HasNotSupportedNameConstraint:
- case X509ChainStatusFlags.HasNotPermittedNameConstraint:
- case X509ChainStatusFlags.HasExcludedNameConstraint:
- result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreInvalidName) != 0);
- break;
- case X509ChainStatusFlags.InvalidExtension:
- // not sure ?!?
- result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreWrongUsage) != 0);
- break;
- //
- // ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreRootRevocationUnknown) != 0)
- // ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreEndRevocationUnknown) != 0)
- case X509ChainStatusFlags.CtlNotTimeValid:
- result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreCtlNotTimeValid) != 0);
- break;
- case X509ChainStatusFlags.CtlNotSignatureValid:
- // ?
- break;
- // ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreCtlSignerRevocationUnknown) != 0);
- case X509ChainStatusFlags.CtlNotValidForUsage:
- // FIXME - does IgnoreWrongUsage apply to CTL (it doesn't have Ctl in it's name like the others)
- result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreWrongUsage) != 0);
- break;
- default:
- result = false;
- break;
- }
- // once we have one failure there's no need to check further
- if (!result)
- return false;
- }
-
- // every "problem" was excluded
- return true;
+ return Impl.Build (certificate);
}
public void Reset ()
{
- // note: this call doesn't Reset the X509ChainPolicy
- if ((status != null) && (status.Length != 0))
- status = null;
- if (elements.Count > 0)
- elements.Clear ();
- if (user_root_store != null) {
- user_root_store.Close ();
- user_root_store = null;
- }
- if (root_store != null) {
- root_store.Close ();
- root_store = null;
- }
- if (user_ca_store != null) {
- user_ca_store.Close ();
- user_ca_store = null;
- }
- if (ca_store != null) {
- ca_store.Close ();
- ca_store = null;
- }
- roots = null;
- cas = null;
- collection = null;
- bce_restriction = null;
- working_public_key = null;
+ Impl.Reset ();
}
// static methods
#endif
}
- // private stuff
-
- private X509Certificate2Collection roots;
- private X509Certificate2Collection cas;
- private X509Store root_store;
- private X509Store ca_store;
- private X509Store user_root_store;
- private X509Store user_ca_store;
-
- private X509Certificate2Collection Roots {
- get {
- if (roots == null) {
- X509Certificate2Collection c = new X509Certificate2Collection ();
- X509Store store = LMRootStore;
- if (location == StoreLocation.CurrentUser)
- c.AddRange (UserRootStore.Certificates);
- c.AddRange (store.Certificates);
- roots = c;
- }
- return roots;
- }
- }
-
- private X509Certificate2Collection CertificateAuthorities {
- get {
- if (cas == null) {
- X509Certificate2Collection c = new X509Certificate2Collection ();
- X509Store store = LMCAStore;
- if (location == StoreLocation.CurrentUser)
- c.AddRange (UserCAStore.Certificates);
- c.AddRange (store.Certificates);
- cas = c;
- }
- return cas;
- }
- }
-
- private X509Store LMRootStore {
- get {
- if (root_store == null) {
- root_store = new X509Store (StoreName.Root, StoreLocation.LocalMachine);
- try {
- root_store.Open (OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
- } catch {
- }
- }
- return root_store;
- }
- }
-
- private X509Store UserRootStore {
- get {
- if (user_root_store == null) {
- user_root_store = new X509Store (StoreName.Root, StoreLocation.CurrentUser);
- try {
- user_root_store.Open (OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
- } catch {
- }
- }
- return user_root_store;
- }
- }
-
- private X509Store LMCAStore {
- get {
- if (ca_store == null) {
- ca_store = new X509Store (StoreName.CertificateAuthority, StoreLocation.LocalMachine);
- try {
- ca_store.Open (OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
- } catch {
- }
- }
- return ca_store;
- }
- }
-
- private X509Store UserCAStore {
- get {
- if (user_ca_store == null) {
- user_ca_store = new X509Store (StoreName.CertificateAuthority, StoreLocation.CurrentUser);
- try {
- user_ca_store.Open (OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
- } catch {
- }
- }
- return user_ca_store;
- }
- }
- // *** certificate chain/path building stuff ***
-
- private X509Certificate2Collection collection;
-
- // we search local user (default) or machine certificate store
- // and in the extra certificate supplied in ChainPolicy.ExtraStore
- private X509Certificate2Collection CertificateCollection {
- get {
- if (collection == null) {
- collection = new X509Certificate2Collection (ChainPolicy.ExtraStore);
- collection.AddRange (Roots);
- collection.AddRange (CertificateAuthorities);
- }
- return collection;
- }
- }
-
- // This is a non-recursive chain/path building algorithm.
- //
- // At this stage we only checks for PartialChain, Cyclic and UntrustedRoot errors are they
- // affect the path building (other errors are verification errors).
- //
- // Note that the order match the one we need to match MS and not the one defined in RFC3280,
- // we also include the trusted root certificate (trust anchor in RFC3280) in the list.
- // (this isn't an issue, just keep that in mind if you look at the source and the RFC)
- private X509ChainStatusFlags BuildChainFrom (X509Certificate2 certificate)
- {
- elements.Add (certificate);
-
- while (!IsChainComplete (certificate)) {
- certificate = FindParent (certificate);
-
- if (certificate == null)
- return X509ChainStatusFlags.PartialChain;
-
- if (elements.Contains (certificate))
- return X509ChainStatusFlags.Cyclic;
-
- elements.Add (certificate);
- }
-
- // roots may be supplied (e.g. in the ExtraStore) so we need to confirm their
- // trustiness (what a cute word) in the trusted root collection
- if (!Roots.Contains (certificate))
- elements [elements.Count - 1].StatusFlags |= X509ChainStatusFlags.UntrustedRoot;
-
- return X509ChainStatusFlags.NoError;
- }
-
-
- private X509Certificate2 SelectBestFromCollection (X509Certificate2 child, X509Certificate2Collection c)
- {
- switch (c.Count) {
- case 0:
- return null;
- case 1:
- return c [0];
- default:
- // multiple candidate, keep only the ones that are still valid
- X509Certificate2Collection time_valid = c.Find (X509FindType.FindByTimeValid, ChainPolicy.VerificationTime, false);
- switch (time_valid.Count) {
- case 0:
- // that's too restrictive, let's revert and try another thing...
- time_valid = c;
- break;
- case 1:
- return time_valid [0];
- default:
- break;
- }
-
- // again multiple candidates, let's find the AKI that match the SKI (if we have one)
- string aki = GetAuthorityKeyIdentifier (child);
- if (String.IsNullOrEmpty (aki)) {
- return time_valid [0]; // FIXME: out of luck, you get the first one
- }
- foreach (X509Certificate2 parent in time_valid) {
- string ski = GetSubjectKeyIdentifier (parent);
- // if both id are available then they must match
- if (aki == ski)
- return parent;
- }
- return time_valid [0]; // FIXME: out of luck, you get the first one
- }
- }
-
- private X509Certificate2 FindParent (X509Certificate2 certificate)
- {
- X509Certificate2Collection subset = CertificateCollection.Find (X509FindType.FindBySubjectDistinguishedName, certificate.Issuer, false);
- string aki = GetAuthorityKeyIdentifier (certificate);
- if ((aki != null) && (aki.Length > 0)) {
- subset.AddRange (CertificateCollection.Find (X509FindType.FindBySubjectKeyIdentifier, aki, false));
- }
- X509Certificate2 parent = SelectBestFromCollection (certificate, subset);
- // if parent==certificate we're looping but it's not (probably) a bug and not a true cyclic (over n certs)
- return certificate.Equals (parent) ? null : parent;
- }
-
- private bool IsChainComplete (X509Certificate2 certificate)
+ public void Dispose ()
{
- // the chain is complete if we have a self-signed certificate
- if (!IsSelfIssued (certificate))
- return false;
-
- // we're very limited to what we can do without certificate extensions
- if (certificate.Version < 3)
- return true;
-
- // check that Authority Key Identifier == Subject Key Identifier
- // e.g. it will be different if a self-signed certificate is part (not the end) of the chain
- string ski = GetSubjectKeyIdentifier (certificate);
- if (String.IsNullOrEmpty (ski))
- return true;
- string aki = GetAuthorityKeyIdentifier (certificate);
- if (String.IsNullOrEmpty (aki))
- return true;
- // if both id are available then they must match
- return (aki == ski);
+ Dispose (true);
+ GC.SuppressFinalize (this);
}
- // check for "self-issued" certificate - without verifying the signature
- // note that self-issued doesn't always mean it's a root certificate!
- private bool IsSelfIssued (X509Certificate2 certificate)
+ protected virtual void Dispose (bool disposing)
{
- return (certificate.Issuer == certificate.Subject);
- }
-
-
- // *** certificate chain/path validation stuff ***
-
- // Currently a subset of RFC3280 (hopefully a full implementation someday)
- private void ValidateChain (X509ChainStatusFlags flag)
- {
- // 'n' should be the root certificate...
- int n = elements.Count - 1;
- X509Certificate2 certificate = elements [n].Certificate;
-
- // ... and, if so, must be treated outside the chain...
- if (((flag & X509ChainStatusFlags.PartialChain) == 0)) {
- Process (n);
- // deal with the case where the chain == the root certificate
- // (which isn't for RFC3280) part of the chain
- if (n == 0) {
- elements [0].UncompressFlags ();
- return;
- }
- // skip the root certificate when processing the chain (in 6.1.3)
- n--;
- }
- // ... unless the chain is a partial one (then we start with that one)
-
- // 6.1.1 - Inputs
- // 6.1.1.a - a prospective certificate path of length n (i.e. elements)
- // 6.1.1.b - the current date/time (i.e. ChainPolicy.VerificationTime)
- // 6.1.1.c - user-initial-policy-set (i.e. ChainPolicy.CertificatePolicy)
- // 6.1.1.d - the trust anchor information (i.e. certificate, unless it's a partial chain)
- // 6.1.1.e - initial-policy-mapping-inhibit (NOT SUPPORTED BY THE API)
- // 6.1.1.f - initial-explicit-policy (NOT SUPPORTED BY THE API)
- // 6.1.1.g - initial-any-policy-inhibit (NOT SUPPORTED BY THE API)
-
- // 6.1.2 - Initialization (incomplete)
- // 6.1.2.a-f - policy stuff, some TODO, some not supported
- // 6.1.2.g - working public key algorithm
-// working_public_key_algorithm = certificate.PublicKey.Oid.Value;
- // 6.1.2.h-i - our key contains both the "working public key" and "working public key parameters" data
- working_public_key = certificate.PublicKey.Key;
- // 6.1.2.j - working issuer name
- working_issuer_name = certificate.IssuerName;
- // 6.1.2.k - this integer is initialized to n, is decremented for each non-self-issued, certificate and
- // may be reduced to the value in the path length constraint field
- max_path_length = n;
-
- // 6.1.3 - Basic Certificate Processing
- // note: loop looks reversed (the list is) but we process this part just like RFC3280 does
- for (int i = n; i > 0; i--) {
- Process (i);
- // 6.1.4 - preparation for certificate i+1 (for not with i+1, or i-1 in our loop)
- PrepareForNextCertificate (i);
+ if (impl != null) {
+ impl.Dispose ();
+ impl = null;
}
- Process (0);
-
- // 6.1.3.a.3 - revocation checks
- CheckRevocationOnChain (flag);
-
- // 6.1.5 - Wrap-up procedure
- WrapUp ();
}
- private void Process (int n)
+ ~X509Chain ()
{
- X509ChainElement element = elements [n];
- X509Certificate2 certificate = element.Certificate;
-
- // pre-step: DSA certificates may inherit the parameters of their CA
- if ((n != elements.Count - 1) && (certificate.MonoCertificate.KeyAlgorithm == "1.2.840.10040.4.1")) {
- if (certificate.MonoCertificate.KeyAlgorithmParameters == null) {
- X509Certificate2 parent = elements [n+1].Certificate;
- certificate.MonoCertificate.KeyAlgorithmParameters = parent.MonoCertificate.KeyAlgorithmParameters;
- }
- }
-
- bool root = (working_public_key == null);
- // 6.1.3.a.1 - check signature (with special case to deal with root certificates)
- if (!IsSignedWith (certificate, root ? certificate.PublicKey.Key : working_public_key)) {
- // another special case where only an end-entity is available and can't be verified.
- // In this case we do not report an invalid signature (since this is unknown)
- if (root || (n != elements.Count - 1) || IsSelfIssued (certificate)) {
- element.StatusFlags |= X509ChainStatusFlags.NotSignatureValid;
- }
- }
-
- // 6.1.3.a.2 - check validity period
- if ((ChainPolicy.VerificationTime < certificate.NotBefore) ||
- (ChainPolicy.VerificationTime > certificate.NotAfter)) {
- element.StatusFlags |= X509ChainStatusFlags.NotTimeValid;
- }
- // TODO - for X509ChainStatusFlags.NotTimeNested (needs global structure)
-
- // note: most of them don't apply to the root certificate
- if (root) {
- return;
- }
-
- // 6.1.3.a.3 - revocation check (we're doing at the last stage)
- // note: you revoke a trusted root by removing it from your trusted store (i.e. no CRL can do this job)
-
- // 6.1.3.a.4 - check certificate issuer name
- if (!X500DistinguishedName.AreEqual (certificate.IssuerName, working_issuer_name)) {
- // NOTE: this is not the "right" error flag, but it's the closest one defined
- element.StatusFlags |= X509ChainStatusFlags.InvalidNameConstraints;
- }
-
- if (!IsSelfIssued (certificate) && (n != 0)) {
- // TODO 6.1.3.b - subject name in the permitted_subtrees ...
- // TODO 6.1.3.c - subject name not within excluded_subtrees...
-
- // TODO - check for X509ChainStatusFlags.InvalidNameConstraint
- // TODO - check for X509ChainStatusFlags.HasNotSupportedNameConstraint
- // TODO - check for X509ChainStatusFlags.HasNotPermittedNameConstraint
- // TODO - check for X509ChainStatusFlags.HasExcludedNameConstraint
- }
-
- // TODO 6.1.3.d - check if certificate policies extension is present
- //if (false) {
- // TODO - for X509ChainStatusFlags.InvalidPolicyConstraints
- // using X509ChainPolicy.ApplicationPolicy and X509ChainPolicy.CertificatePolicy
-
- // TODO - check for X509ChainStatusFlags.NoIssuanceChainPolicy
-
- //} else {
- // TODO 6.1.3.e - set valid_policy_tree to NULL
- //}
-
- // TODO 6.1.3.f - verify explict_policy > 0 if valid_policy_tree != NULL
- }
-
- // CTL == Certificate Trust List / NOT SUPPORTED
- // TODO - check for X509ChainStatusFlags.CtlNotTimeValid
- // TODO - check for X509ChainStatusFlags.CtlNotSignatureValid
- // TODO - check for X509ChainStatusFlags.CtlNotValidForUsage
-
- private void PrepareForNextCertificate (int n)
- {
- X509ChainElement element = elements [n];
- X509Certificate2 certificate = element.Certificate;
-
- // TODO 6.1.4.a-b
-
- // 6.1.4.c
- working_issuer_name = certificate.SubjectName;
- // 6.1.4.d-e - our key includes both the public key and it's parameters
- working_public_key = certificate.PublicKey.Key;
- // 6.1.4.f
-// working_public_key_algorithm = certificate.PublicKey.Oid.Value;
-
- // TODO 6.1.4.g-j
-
- // 6.1.4.k - Verify that the certificate is a CA certificate
- X509BasicConstraintsExtension bce = (certificate.Extensions["2.5.29.19"] as X509BasicConstraintsExtension);
- if (bce != null) {
- if (!bce.CertificateAuthority) {
- element.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints;
- }
- } else if (certificate.Version >= 3) {
- // recent (v3+) CA certificates must include BCE
- element.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints;
- }
-
- // 6.1.4.l - if the certificate isn't self-issued...
- if (!IsSelfIssued (certificate)) {
- // ... verify that max_path_length > 0
- if (max_path_length > 0) {
- max_path_length--;
- } else {
- // to match MS the reported status must be against the certificate
- // with the BCE and not where the path is too long. It also means
- // that this condition has to be reported only once
- if (bce_restriction != null) {
- bce_restriction.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints;
- }
- }
- }
-
- // 6.1.4.m - if pathLengthConstraint is present...
- if ((bce != null) && (bce.HasPathLengthConstraint)) {
- // ... and is less that max_path_length, set max_path_length to it's value
- if (bce.PathLengthConstraint < max_path_length) {
- max_path_length = bce.PathLengthConstraint;
- bce_restriction = element;
- }
- }
-
- // 6.1.4.n - if key usage extension is present...
- X509KeyUsageExtension kue = (certificate.Extensions["2.5.29.15"] as X509KeyUsageExtension);
- if (kue != null) {
- // ... verify keyCertSign is set
- X509KeyUsageFlags success = X509KeyUsageFlags.KeyCertSign;
- if ((kue.KeyUsages & success) != success)
- element.StatusFlags |= X509ChainStatusFlags.NotValidForUsage;
- }
-
- // 6.1.4.o - recognize and process other critical extension present in the certificate
- ProcessCertificateExtensions (element);
- }
-
- private void WrapUp ()
- {
- X509ChainElement element = elements [0];
- X509Certificate2 certificate = element.Certificate;
-
- // 6.1.5.a - TODO if certificate n (our 0) wasn't self issued and explicit_policy != 0
- if (IsSelfIssued (certificate)) {
- // TODO... decrement explicit_policy by 1
- }
-
- // 6.1.5.b - TODO
-
- // 6.1.5.c,d,e - not required by the X509Chain implementation
-
- // 6.1.5.f - recognize and process other critical extension present in the certificate
- ProcessCertificateExtensions (element);
-
- // 6.1.5.g - TODO
-
- // uncompressed the flags into several elements
- for (int i = elements.Count - 1; i >= 0; i--) {
- elements [i].UncompressFlags ();
- }
- }
-
- private void ProcessCertificateExtensions (X509ChainElement element)
- {
- foreach (X509Extension ext in element.Certificate.Extensions) {
- if (ext.Critical) {
- switch (ext.Oid.Value) {
- case "2.5.29.15": // X509KeyUsageExtension
- case "2.5.29.19": // X509BasicConstraintsExtension
- // we processed this extension
- break;
- default:
- // note: Under Windows XP MS implementation seems to ignore
- // certificate with unknown critical extensions.
- element.StatusFlags |= X509ChainStatusFlags.InvalidExtension;
- break;
- }
- }
- }
- }
-
- private bool IsSignedWith (X509Certificate2 signed, AsymmetricAlgorithm pubkey)
- {
- if (pubkey == null)
- return false;
- // Sadly X509Certificate2 doesn't expose the signature nor the tbs (to be signed) structure
- MX.X509Certificate mx = signed.MonoCertificate;
- return (mx.VerifySignature (pubkey));
- }
-
- private string GetSubjectKeyIdentifier (X509Certificate2 certificate)
- {
- X509SubjectKeyIdentifierExtension ski = (certificate.Extensions["2.5.29.14"] as X509SubjectKeyIdentifierExtension);
- return (ski == null) ? String.Empty : ski.SubjectKeyIdentifier;
- }
-
- // System.dll v2 doesn't have a class to deal with the AuthorityKeyIdentifier extension
- static string GetAuthorityKeyIdentifier (X509Certificate2 certificate)
- {
- return GetAuthorityKeyIdentifier (certificate.MonoCertificate.Extensions ["2.5.29.35"]);
- }
-
- // but anyway System.dll v2 doesn't expose CRL in any way so...
- static string GetAuthorityKeyIdentifier (MX.X509Crl crl)
- {
- return GetAuthorityKeyIdentifier (crl.Extensions ["2.5.29.35"]);
- }
-
- static string GetAuthorityKeyIdentifier (MX.X509Extension ext)
- {
- if (ext == null)
- return String.Empty;
- MX.Extensions.AuthorityKeyIdentifierExtension aki = new MX.Extensions.AuthorityKeyIdentifierExtension (ext);
- byte[] id = aki.Identifier;
- if (id == null)
- return String.Empty;
- StringBuilder sb = new StringBuilder ();
- foreach (byte b in id)
- sb.Append (b.ToString ("X02"));
- return sb.ToString ();
- }
-
- // we check the revocation only once we have built the complete chain
- private void CheckRevocationOnChain (X509ChainStatusFlags flag)
- {
- bool partial = ((flag & X509ChainStatusFlags.PartialChain) != 0);
- bool online;
-
- switch (ChainPolicy.RevocationMode) {
- case X509RevocationMode.Online:
- // default
- online = true;
- break;
- case X509RevocationMode.Offline:
- online = false;
- break;
- case X509RevocationMode.NoCheck:
- return;
- default:
- throw new InvalidOperationException (Locale.GetText ("Invalid revocation mode."));
- }
-
- bool unknown = partial;
- // from the root down to the end-entity
- for (int i = elements.Count - 1; i >= 0; i--) {
- bool check = true;
-
- switch (ChainPolicy.RevocationFlag) {
- case X509RevocationFlag.EndCertificateOnly:
- check = (i == 0);
- break;
- case X509RevocationFlag.EntireChain:
- check = true;
- break;
- case X509RevocationFlag.ExcludeRoot:
- // default
- check = (i != (elements.Count - 1));
- // anyway, who's gonna sign that the root is invalid ?
- break;
- }
-
- X509ChainElement element = elements [i];
-
- // we can't assume the revocation status if the certificate is bad (e.g. invalid signature)
- if (!unknown)
- unknown |= ((element.StatusFlags & X509ChainStatusFlags.NotSignatureValid) != 0);
-
- if (unknown) {
- // we can skip the revocation checks as we can't be sure of them anyway
- element.StatusFlags |= X509ChainStatusFlags.RevocationStatusUnknown;
- element.StatusFlags |= X509ChainStatusFlags.OfflineRevocation;
- } else if (check && !partial && !IsSelfIssued (element.Certificate)) {
- // check for revocation (except for the trusted root and self-issued certs)
- element.StatusFlags |= CheckRevocation (element.Certificate, i+1, online);
- // if revoked, then all others following in the chain are unknown...
- unknown |= ((element.StatusFlags & X509ChainStatusFlags.Revoked) != 0);
- }
- }
- }
-
- // This isn't how RFC3280 (section 6.3) deals with CRL, but then we don't (yet) support DP, deltas...
- private X509ChainStatusFlags CheckRevocation (X509Certificate2 certificate, int ca, bool online)
- {
- X509ChainStatusFlags result = X509ChainStatusFlags.RevocationStatusUnknown;
- X509ChainElement element = elements [ca];
- X509Certificate2 ca_cert = element.Certificate;
-
- // find the CRL from the "right" CA
- while (IsSelfIssued (ca_cert) && (ca < elements.Count - 1)) {
- // try with this self-issued
- result = CheckRevocation (certificate, ca_cert, online);
- if (result != X509ChainStatusFlags.RevocationStatusUnknown)
- break;
- ca++;
- element = elements [ca];
- ca_cert = element.Certificate;
- }
- if (result == X509ChainStatusFlags.RevocationStatusUnknown)
- result = CheckRevocation (certificate, ca_cert, online);
- return result;
- }
-
- private X509ChainStatusFlags CheckRevocation (X509Certificate2 certificate, X509Certificate2 ca_cert, bool online)
- {
- // change this if/when we support OCSP
- X509KeyUsageExtension kue = (ca_cert.Extensions["2.5.29.15"] as X509KeyUsageExtension);
- if (kue != null) {
- // ... verify CrlSign is set
- X509KeyUsageFlags success = X509KeyUsageFlags.CrlSign;
- if ((kue.KeyUsages & success) != success) {
- // FIXME - we should try to find an alternative CA that has the CrlSign bit
- return X509ChainStatusFlags.RevocationStatusUnknown;
- }
- }
-
- MX.X509Crl crl = FindCrl (ca_cert);
-
- if ((crl == null) && online) {
- // FIXME - download and install new CRL
- // then you get a second chance
- // crl = FindCrl (ca_cert, ref valid, ref out_of_date);
-
- // We need to get the subjectAltName and an URI from there (or use OCSP)
- // X509KeyUsageExtension subjectAltName = (ca_cert.Extensions["2.5.29.17"] as X509KeyUsageExtension);
- }
-
- if (crl != null) {
- // validate the digital signature on the CRL using the CA public key
- // note #1: we can't use X509Crl.VerifySignature(X509Certificate) because it duplicates
- // checks and we loose the "why" of the failure
- // note #2: we do this before other tests as an invalid signature could be a hacked CRL
- // (so anything within can't be trusted)
- if (!crl.VerifySignature (ca_cert.PublicKey.Key)) {
- return X509ChainStatusFlags.RevocationStatusUnknown;
- }
-
- MX.X509Crl.X509CrlEntry entry = crl.GetCrlEntry (certificate.MonoCertificate);
- if (entry != null) {
- // We have an entry for this CRL that includes an unknown CRITICAL extension
- // See [X.509 7.3] NOTE 4
- if (!ProcessCrlEntryExtensions (entry))
- return X509ChainStatusFlags.Revoked;
-
- // FIXME - a little more is involved
- if (entry.RevocationDate <= ChainPolicy.VerificationTime)
- return X509ChainStatusFlags.Revoked;
- }
-
- // are we overdue for a CRL update ? if so we can't be sure of any certificate status
- if (crl.NextUpdate < ChainPolicy.VerificationTime)
- return X509ChainStatusFlags.RevocationStatusUnknown | X509ChainStatusFlags.OfflineRevocation;
-
- // we have a CRL that includes an unknown CRITICAL extension
- // we put this check at the end so we do not "hide" any Revoked flags
- if (!ProcessCrlExtensions (crl)) {
- return X509ChainStatusFlags.RevocationStatusUnknown;
- }
- } else {
- return X509ChainStatusFlags.RevocationStatusUnknown;
- }
-
- return X509ChainStatusFlags.NoError;
- }
-
- static MX.X509Crl CheckCrls (string subject, string ski, MX.X509Store store)
- {
- if (store == null)
- return null;
-
- var crls = store.Crls;
- foreach (MX.X509Crl crl in crls) {
- if (crl.IssuerName == subject && (ski.Length == 0 || ski == GetAuthorityKeyIdentifier (crl)))
- return crl;
- }
- return null; // No CRL found
- }
-
- private MX.X509Crl FindCrl (X509Certificate2 caCertificate)
- {
- string subject = caCertificate.SubjectName.Decode (X500DistinguishedNameFlags.None);
- string ski = GetSubjectKeyIdentifier (caCertificate);
-
- // consider that the LocalMachine directories could not exists... and cannot be created by the user
- MX.X509Crl result = CheckCrls (subject, ski, LMCAStore.Store);
- if (result != null)
- return result;
- if (location == StoreLocation.CurrentUser) {
- result = CheckCrls (subject, ski, UserCAStore.Store);
- if (result != null)
- return result;
- }
-
- // consider that the LocalMachine directories could not exists... and cannot be created by the user
- result = CheckCrls (subject, ski, LMRootStore.Store);
- if (result != null)
- return result;
- if (location == StoreLocation.CurrentUser) {
- result = CheckCrls (subject, ski, UserRootStore.Store);
- if (result != null)
- return result;
- }
- return null;
- }
-
- private bool ProcessCrlExtensions (MX.X509Crl crl)
- {
- foreach (MX.X509Extension ext in crl.Extensions) {
- if (ext.Critical) {
- switch (ext.Oid) {
- case "2.5.29.20": // cRLNumber
- case "2.5.29.35": // authorityKeyIdentifier
- // we processed/know about this extension
- break;
- default:
- return false;
- }
- }
- }
- return true;
- }
-
- private bool ProcessCrlEntryExtensions (MX.X509Crl.X509CrlEntry entry)
- {
- foreach (MX.X509Extension ext in entry.Extensions) {
- if (ext.Critical) {
- switch (ext.Oid) {
- case "2.5.29.21": // cRLReason
- // we processed/know about this extension
- break;
- default:
- return false;
- }
- }
- }
- return true;
+ Dispose (false);
}
}
}
--- /dev/null
+//
+// X509ChainImpl.cs
+//
+// Authors:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (C) 2016 Xamarin, Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if SECURITY_DEP
+
+namespace System.Security.Cryptography.X509Certificates
+{
+ internal abstract class X509ChainImpl : IDisposable
+ {
+ public abstract bool IsValid {
+ get;
+ }
+
+ public abstract IntPtr Handle {
+ get;
+ }
+
+ protected void ThrowIfContextInvalid ()
+ {
+ if (!IsValid)
+ throw X509Helper2.GetInvalidChainContextException ();
+ }
+
+ public abstract X509ChainElementCollection ChainElements {
+ get;
+ }
+
+ public abstract X509ChainPolicy ChainPolicy {
+ get; set;
+ }
+
+ public abstract X509ChainStatus[] ChainStatus {
+ get;
+ }
+
+ public abstract bool Build (X509Certificate2 certificate);
+
+ public abstract void Reset ();
+
+ public void Dispose ()
+ {
+ Dispose (true);
+ GC.SuppressFinalize (this);
+ }
+
+ protected virtual void Dispose (bool disposing)
+ {
+ }
+
+ ~X509ChainImpl ()
+ {
+ Dispose (false);
+ }
+ }
+}
+
+#endif
--- /dev/null
+//
+// System.Security.Cryptography.X509Certificates.X509ChainImplMono
+//
+// Author:
+// Sebastien Pouliot <sebastien@ximian.com>
+//
+// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
+// Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com)
+// Copyright (C) 2011 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if SECURITY_DEP
+
+#if MONO_SECURITY_ALIAS
+extern alias MonoSecurity;
+using MX = MonoSecurity::Mono.Security.X509;
+#else
+using MX = Mono.Security.X509;
+#endif
+
+using System.Collections;
+using System.Text;
+
+namespace System.Security.Cryptography.X509Certificates {
+
+ internal class X509ChainImplMono : X509ChainImpl
+ {
+ private StoreLocation location;
+ private X509ChainElementCollection elements;
+ private X509ChainPolicy policy;
+ private X509ChainStatus[] status;
+
+ static X509ChainStatus[] Empty = new X509ChainStatus [0];
+
+ // RFC3280 variables
+ private int max_path_length;
+ private X500DistinguishedName working_issuer_name;
+// private string working_public_key_algorithm;
+ private AsymmetricAlgorithm working_public_key;
+
+ // other flags
+ private X509ChainElement bce_restriction;
+
+ // constructors
+
+ public X509ChainImplMono ()
+ : this (false)
+ {
+ }
+
+ public X509ChainImplMono (bool useMachineContext)
+ {
+ location = useMachineContext ? StoreLocation.LocalMachine : StoreLocation.CurrentUser;
+ elements = new X509ChainElementCollection ();
+ policy = new X509ChainPolicy ();
+ }
+
+ [MonoTODO ("Mono's X509Chain is fully managed. All handles are invalid.")]
+ public X509ChainImplMono (IntPtr chainContext)
+ {
+ // CryptoAPI compatibility (unmanaged handle)
+ throw new NotSupportedException ();
+ }
+
+ public override bool IsValid {
+ get { return true; }
+ }
+
+ public override IntPtr Handle {
+ get { return IntPtr.Zero; }
+ }
+
+ // properties
+
+ public override X509ChainElementCollection ChainElements {
+ get { return elements; }
+ }
+
+ public override X509ChainPolicy ChainPolicy {
+ get { return policy; }
+ set { policy = value; }
+ }
+
+ public override X509ChainStatus[] ChainStatus {
+ get {
+ if (status == null)
+ return Empty;
+ return status;
+ }
+ }
+
+ // methods
+
+ [MonoTODO ("Not totally RFC3280 compliant, but neither is MS implementation...")]
+ public override bool Build (X509Certificate2 certificate)
+ {
+ if (certificate == null)
+ throw new ArgumentException ("certificate");
+
+ Reset ();
+ X509ChainStatusFlags flag;
+ try {
+ flag = BuildChainFrom (certificate);
+ ValidateChain (flag);
+ }
+ catch (CryptographicException ce) {
+ throw new ArgumentException ("certificate", ce);
+ }
+
+ X509ChainStatusFlags total = X509ChainStatusFlags.NoError;
+ ArrayList list = new ArrayList ();
+ // build "global" ChainStatus from the ChainStatus of every ChainElements
+ foreach (X509ChainElement ce in elements) {
+ foreach (X509ChainStatus cs in ce.ChainElementStatus) {
+ // we MUST avoid duplicates in the "global" list
+ if ((total & cs.Status) != cs.Status) {
+ list.Add (cs);
+ total |= cs.Status;
+ }
+ }
+ }
+ // and if required add some
+ if (flag != X509ChainStatusFlags.NoError) {
+ list.Insert (0, new X509ChainStatus (flag));
+ }
+ status = (X509ChainStatus[]) list.ToArray (typeof (X509ChainStatus));
+
+ // (fast path) this ignore everything we have checked
+ if ((status.Length == 0) || (ChainPolicy.VerificationFlags == X509VerificationFlags.AllFlags))
+ return true;
+
+ bool result = true;
+ // now check if exclude some verification for the "end result" (boolean)
+ foreach (X509ChainStatus cs in status) {
+ switch (cs.Status) {
+ case X509ChainStatusFlags.UntrustedRoot:
+ case X509ChainStatusFlags.PartialChain:
+ result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.AllowUnknownCertificateAuthority) != 0);
+ break;
+ case X509ChainStatusFlags.NotTimeValid:
+ result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreNotTimeValid) != 0);
+ break;
+ // FIXME - from here we needs new test cases for all cases
+ case X509ChainStatusFlags.NotTimeNested:
+ result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreNotTimeNested) != 0);
+ break;
+ case X509ChainStatusFlags.InvalidBasicConstraints:
+ result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreInvalidBasicConstraints) != 0);
+ break;
+ case X509ChainStatusFlags.InvalidPolicyConstraints:
+ case X509ChainStatusFlags.NoIssuanceChainPolicy:
+ result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreInvalidPolicy) != 0);
+ break;
+ case X509ChainStatusFlags.InvalidNameConstraints:
+ case X509ChainStatusFlags.HasNotSupportedNameConstraint:
+ case X509ChainStatusFlags.HasNotPermittedNameConstraint:
+ case X509ChainStatusFlags.HasExcludedNameConstraint:
+ result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreInvalidName) != 0);
+ break;
+ case X509ChainStatusFlags.InvalidExtension:
+ // not sure ?!?
+ result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreWrongUsage) != 0);
+ break;
+ //
+ // ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreRootRevocationUnknown) != 0)
+ // ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreEndRevocationUnknown) != 0)
+ case X509ChainStatusFlags.CtlNotTimeValid:
+ result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreCtlNotTimeValid) != 0);
+ break;
+ case X509ChainStatusFlags.CtlNotSignatureValid:
+ // ?
+ break;
+ // ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreCtlSignerRevocationUnknown) != 0);
+ case X509ChainStatusFlags.CtlNotValidForUsage:
+ // FIXME - does IgnoreWrongUsage apply to CTL (it doesn't have Ctl in it's name like the others)
+ result &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreWrongUsage) != 0);
+ break;
+ default:
+ result = false;
+ break;
+ }
+ // once we have one failure there's no need to check further
+ if (!result)
+ return false;
+ }
+
+ // every "problem" was excluded
+ return true;
+ }
+
+ public override void Reset ()
+ {
+ // note: this call doesn't Reset the X509ChainPolicy
+ if ((status != null) && (status.Length != 0))
+ status = null;
+ if (elements.Count > 0)
+ elements.Clear ();
+ if (user_root_store != null) {
+ user_root_store.Close ();
+ user_root_store = null;
+ }
+ if (root_store != null) {
+ root_store.Close ();
+ root_store = null;
+ }
+ if (user_ca_store != null) {
+ user_ca_store.Close ();
+ user_ca_store = null;
+ }
+ if (ca_store != null) {
+ ca_store.Close ();
+ ca_store = null;
+ }
+ roots = null;
+ cas = null;
+ collection = null;
+ bce_restriction = null;
+ working_public_key = null;
+ }
+
+ // private stuff
+
+ private X509Certificate2Collection roots;
+ private X509Certificate2Collection cas;
+ private X509Store root_store;
+ private X509Store ca_store;
+ private X509Store user_root_store;
+ private X509Store user_ca_store;
+
+ private X509Certificate2Collection Roots {
+ get {
+ if (roots == null) {
+ X509Certificate2Collection c = new X509Certificate2Collection ();
+ X509Store store = LMRootStore;
+ if (location == StoreLocation.CurrentUser)
+ c.AddRange (UserRootStore.Certificates);
+ c.AddRange (store.Certificates);
+ roots = c;
+ }
+ return roots;
+ }
+ }
+
+ private X509Certificate2Collection CertificateAuthorities {
+ get {
+ if (cas == null) {
+ X509Certificate2Collection c = new X509Certificate2Collection ();
+ X509Store store = LMCAStore;
+ if (location == StoreLocation.CurrentUser)
+ c.AddRange (UserCAStore.Certificates);
+ c.AddRange (store.Certificates);
+ cas = c;
+ }
+ return cas;
+ }
+ }
+
+ private X509Store LMRootStore {
+ get {
+ if (root_store == null) {
+ root_store = new X509Store (StoreName.Root, StoreLocation.LocalMachine);
+ try {
+ root_store.Open (OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
+ } catch {
+ }
+ }
+ return root_store;
+ }
+ }
+
+ private X509Store UserRootStore {
+ get {
+ if (user_root_store == null) {
+ user_root_store = new X509Store (StoreName.Root, StoreLocation.CurrentUser);
+ try {
+ user_root_store.Open (OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
+ } catch {
+ }
+ }
+ return user_root_store;
+ }
+ }
+
+ private X509Store LMCAStore {
+ get {
+ if (ca_store == null) {
+ ca_store = new X509Store (StoreName.CertificateAuthority, StoreLocation.LocalMachine);
+ try {
+ ca_store.Open (OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
+ } catch {
+ }
+ }
+ return ca_store;
+ }
+ }
+
+ private X509Store UserCAStore {
+ get {
+ if (user_ca_store == null) {
+ user_ca_store = new X509Store (StoreName.CertificateAuthority, StoreLocation.CurrentUser);
+ try {
+ user_ca_store.Open (OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
+ } catch {
+ }
+ }
+ return user_ca_store;
+ }
+ }
+ // *** certificate chain/path building stuff ***
+
+ private X509Certificate2Collection collection;
+
+ // we search local user (default) or machine certificate store
+ // and in the extra certificate supplied in ChainPolicy.ExtraStore
+ private X509Certificate2Collection CertificateCollection {
+ get {
+ if (collection == null) {
+ collection = new X509Certificate2Collection (ChainPolicy.ExtraStore);
+ collection.AddRange (Roots);
+ collection.AddRange (CertificateAuthorities);
+ }
+ return collection;
+ }
+ }
+
+ // This is a non-recursive chain/path building algorithm.
+ //
+ // At this stage we only checks for PartialChain, Cyclic and UntrustedRoot errors are they
+ // affect the path building (other errors are verification errors).
+ //
+ // Note that the order match the one we need to match MS and not the one defined in RFC3280,
+ // we also include the trusted root certificate (trust anchor in RFC3280) in the list.
+ // (this isn't an issue, just keep that in mind if you look at the source and the RFC)
+ private X509ChainStatusFlags BuildChainFrom (X509Certificate2 certificate)
+ {
+ elements.Add (certificate);
+
+ while (!IsChainComplete (certificate)) {
+ certificate = FindParent (certificate);
+
+ if (certificate == null)
+ return X509ChainStatusFlags.PartialChain;
+
+ if (elements.Contains (certificate))
+ return X509ChainStatusFlags.Cyclic;
+
+ elements.Add (certificate);
+ }
+
+ // roots may be supplied (e.g. in the ExtraStore) so we need to confirm their
+ // trustiness (what a cute word) in the trusted root collection
+ if (!Roots.Contains (certificate))
+ elements [elements.Count - 1].StatusFlags |= X509ChainStatusFlags.UntrustedRoot;
+
+ return X509ChainStatusFlags.NoError;
+ }
+
+
+ private X509Certificate2 SelectBestFromCollection (X509Certificate2 child, X509Certificate2Collection c)
+ {
+ switch (c.Count) {
+ case 0:
+ return null;
+ case 1:
+ return c [0];
+ default:
+ // multiple candidate, keep only the ones that are still valid
+ X509Certificate2Collection time_valid = c.Find (X509FindType.FindByTimeValid, ChainPolicy.VerificationTime, false);
+ switch (time_valid.Count) {
+ case 0:
+ // that's too restrictive, let's revert and try another thing...
+ time_valid = c;
+ break;
+ case 1:
+ return time_valid [0];
+ default:
+ break;
+ }
+
+ // again multiple candidates, let's find the AKI that match the SKI (if we have one)
+ string aki = GetAuthorityKeyIdentifier (child);
+ if (String.IsNullOrEmpty (aki)) {
+ return time_valid [0]; // FIXME: out of luck, you get the first one
+ }
+ foreach (X509Certificate2 parent in time_valid) {
+ string ski = GetSubjectKeyIdentifier (parent);
+ // if both id are available then they must match
+ if (aki == ski)
+ return parent;
+ }
+ return time_valid [0]; // FIXME: out of luck, you get the first one
+ }
+ }
+
+ private X509Certificate2 FindParent (X509Certificate2 certificate)
+ {
+ X509Certificate2Collection subset = CertificateCollection.Find (X509FindType.FindBySubjectDistinguishedName, certificate.Issuer, false);
+ string aki = GetAuthorityKeyIdentifier (certificate);
+ if ((aki != null) && (aki.Length > 0)) {
+ subset.AddRange (CertificateCollection.Find (X509FindType.FindBySubjectKeyIdentifier, aki, false));
+ }
+ X509Certificate2 parent = SelectBestFromCollection (certificate, subset);
+ // if parent==certificate we're looping but it's not (probably) a bug and not a true cyclic (over n certs)
+ return certificate.Equals (parent) ? null : parent;
+ }
+
+ private bool IsChainComplete (X509Certificate2 certificate)
+ {
+ // the chain is complete if we have a self-signed certificate
+ if (!IsSelfIssued (certificate))
+ return false;
+
+ // we're very limited to what we can do without certificate extensions
+ if (certificate.Version < 3)
+ return true;
+
+ // check that Authority Key Identifier == Subject Key Identifier
+ // e.g. it will be different if a self-signed certificate is part (not the end) of the chain
+ string ski = GetSubjectKeyIdentifier (certificate);
+ if (String.IsNullOrEmpty (ski))
+ return true;
+ string aki = GetAuthorityKeyIdentifier (certificate);
+ if (String.IsNullOrEmpty (aki))
+ return true;
+ // if both id are available then they must match
+ return (aki == ski);
+ }
+
+ // check for "self-issued" certificate - without verifying the signature
+ // note that self-issued doesn't always mean it's a root certificate!
+ private bool IsSelfIssued (X509Certificate2 certificate)
+ {
+ return (certificate.Issuer == certificate.Subject);
+ }
+
+
+ // *** certificate chain/path validation stuff ***
+
+ // Currently a subset of RFC3280 (hopefully a full implementation someday)
+ private void ValidateChain (X509ChainStatusFlags flag)
+ {
+ // 'n' should be the root certificate...
+ int n = elements.Count - 1;
+ X509Certificate2 certificate = elements [n].Certificate;
+
+ // ... and, if so, must be treated outside the chain...
+ if (((flag & X509ChainStatusFlags.PartialChain) == 0)) {
+ Process (n);
+ // deal with the case where the chain == the root certificate
+ // (which isn't for RFC3280) part of the chain
+ if (n == 0) {
+ elements [0].UncompressFlags ();
+ return;
+ }
+ // skip the root certificate when processing the chain (in 6.1.3)
+ n--;
+ }
+ // ... unless the chain is a partial one (then we start with that one)
+
+ // 6.1.1 - Inputs
+ // 6.1.1.a - a prospective certificate path of length n (i.e. elements)
+ // 6.1.1.b - the current date/time (i.e. ChainPolicy.VerificationTime)
+ // 6.1.1.c - user-initial-policy-set (i.e. ChainPolicy.CertificatePolicy)
+ // 6.1.1.d - the trust anchor information (i.e. certificate, unless it's a partial chain)
+ // 6.1.1.e - initial-policy-mapping-inhibit (NOT SUPPORTED BY THE API)
+ // 6.1.1.f - initial-explicit-policy (NOT SUPPORTED BY THE API)
+ // 6.1.1.g - initial-any-policy-inhibit (NOT SUPPORTED BY THE API)
+
+ // 6.1.2 - Initialization (incomplete)
+ // 6.1.2.a-f - policy stuff, some TODO, some not supported
+ // 6.1.2.g - working public key algorithm
+// working_public_key_algorithm = certificate.PublicKey.Oid.Value;
+ // 6.1.2.h-i - our key contains both the "working public key" and "working public key parameters" data
+ working_public_key = certificate.PublicKey.Key;
+ // 6.1.2.j - working issuer name
+ working_issuer_name = certificate.IssuerName;
+ // 6.1.2.k - this integer is initialized to n, is decremented for each non-self-issued, certificate and
+ // may be reduced to the value in the path length constraint field
+ max_path_length = n;
+
+ // 6.1.3 - Basic Certificate Processing
+ // note: loop looks reversed (the list is) but we process this part just like RFC3280 does
+ for (int i = n; i > 0; i--) {
+ Process (i);
+ // 6.1.4 - preparation for certificate i+1 (for not with i+1, or i-1 in our loop)
+ PrepareForNextCertificate (i);
+ }
+ Process (0);
+
+ // 6.1.3.a.3 - revocation checks
+ CheckRevocationOnChain (flag);
+
+ // 6.1.5 - Wrap-up procedure
+ WrapUp ();
+ }
+
+ private void Process (int n)
+ {
+ X509ChainElement element = elements [n];
+ X509Certificate2 certificate = element.Certificate;
+
+ // pre-step: DSA certificates may inherit the parameters of their CA
+ if ((n != elements.Count - 1) && (certificate.MonoCertificate.KeyAlgorithm == "1.2.840.10040.4.1")) {
+ if (certificate.MonoCertificate.KeyAlgorithmParameters == null) {
+ X509Certificate2 parent = elements [n+1].Certificate;
+ certificate.MonoCertificate.KeyAlgorithmParameters = parent.MonoCertificate.KeyAlgorithmParameters;
+ }
+ }
+
+ bool root = (working_public_key == null);
+ // 6.1.3.a.1 - check signature (with special case to deal with root certificates)
+ if (!IsSignedWith (certificate, root ? certificate.PublicKey.Key : working_public_key)) {
+ // another special case where only an end-entity is available and can't be verified.
+ // In this case we do not report an invalid signature (since this is unknown)
+ if (root || (n != elements.Count - 1) || IsSelfIssued (certificate)) {
+ element.StatusFlags |= X509ChainStatusFlags.NotSignatureValid;
+ }
+ }
+
+ // 6.1.3.a.2 - check validity period
+ if ((ChainPolicy.VerificationTime < certificate.NotBefore) ||
+ (ChainPolicy.VerificationTime > certificate.NotAfter)) {
+ element.StatusFlags |= X509ChainStatusFlags.NotTimeValid;
+ }
+ // TODO - for X509ChainStatusFlags.NotTimeNested (needs global structure)
+
+ // note: most of them don't apply to the root certificate
+ if (root) {
+ return;
+ }
+
+ // 6.1.3.a.3 - revocation check (we're doing at the last stage)
+ // note: you revoke a trusted root by removing it from your trusted store (i.e. no CRL can do this job)
+
+ // 6.1.3.a.4 - check certificate issuer name
+ if (!X500DistinguishedName.AreEqual (certificate.IssuerName, working_issuer_name)) {
+ // NOTE: this is not the "right" error flag, but it's the closest one defined
+ element.StatusFlags |= X509ChainStatusFlags.InvalidNameConstraints;
+ }
+
+ if (!IsSelfIssued (certificate) && (n != 0)) {
+ // TODO 6.1.3.b - subject name in the permitted_subtrees ...
+ // TODO 6.1.3.c - subject name not within excluded_subtrees...
+
+ // TODO - check for X509ChainStatusFlags.InvalidNameConstraint
+ // TODO - check for X509ChainStatusFlags.HasNotSupportedNameConstraint
+ // TODO - check for X509ChainStatusFlags.HasNotPermittedNameConstraint
+ // TODO - check for X509ChainStatusFlags.HasExcludedNameConstraint
+ }
+
+ // TODO 6.1.3.d - check if certificate policies extension is present
+ //if (false) {
+ // TODO - for X509ChainStatusFlags.InvalidPolicyConstraints
+ // using X509ChainPolicy.ApplicationPolicy and X509ChainPolicy.CertificatePolicy
+
+ // TODO - check for X509ChainStatusFlags.NoIssuanceChainPolicy
+
+ //} else {
+ // TODO 6.1.3.e - set valid_policy_tree to NULL
+ //}
+
+ // TODO 6.1.3.f - verify explict_policy > 0 if valid_policy_tree != NULL
+ }
+
+ // CTL == Certificate Trust List / NOT SUPPORTED
+ // TODO - check for X509ChainStatusFlags.CtlNotTimeValid
+ // TODO - check for X509ChainStatusFlags.CtlNotSignatureValid
+ // TODO - check for X509ChainStatusFlags.CtlNotValidForUsage
+
+ private void PrepareForNextCertificate (int n)
+ {
+ X509ChainElement element = elements [n];
+ X509Certificate2 certificate = element.Certificate;
+
+ // TODO 6.1.4.a-b
+
+ // 6.1.4.c
+ working_issuer_name = certificate.SubjectName;
+ // 6.1.4.d-e - our key includes both the public key and it's parameters
+ working_public_key = certificate.PublicKey.Key;
+ // 6.1.4.f
+// working_public_key_algorithm = certificate.PublicKey.Oid.Value;
+
+ // TODO 6.1.4.g-j
+
+ // 6.1.4.k - Verify that the certificate is a CA certificate
+ X509BasicConstraintsExtension bce = (certificate.Extensions["2.5.29.19"] as X509BasicConstraintsExtension);
+ if (bce != null) {
+ if (!bce.CertificateAuthority) {
+ element.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints;
+ }
+ } else if (certificate.Version >= 3) {
+ // recent (v3+) CA certificates must include BCE
+ element.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints;
+ }
+
+ // 6.1.4.l - if the certificate isn't self-issued...
+ if (!IsSelfIssued (certificate)) {
+ // ... verify that max_path_length > 0
+ if (max_path_length > 0) {
+ max_path_length--;
+ } else {
+ // to match MS the reported status must be against the certificate
+ // with the BCE and not where the path is too long. It also means
+ // that this condition has to be reported only once
+ if (bce_restriction != null) {
+ bce_restriction.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints;
+ }
+ }
+ }
+
+ // 6.1.4.m - if pathLengthConstraint is present...
+ if ((bce != null) && (bce.HasPathLengthConstraint)) {
+ // ... and is less that max_path_length, set max_path_length to it's value
+ if (bce.PathLengthConstraint < max_path_length) {
+ max_path_length = bce.PathLengthConstraint;
+ bce_restriction = element;
+ }
+ }
+
+ // 6.1.4.n - if key usage extension is present...
+ X509KeyUsageExtension kue = (certificate.Extensions["2.5.29.15"] as X509KeyUsageExtension);
+ if (kue != null) {
+ // ... verify keyCertSign is set
+ X509KeyUsageFlags success = X509KeyUsageFlags.KeyCertSign;
+ if ((kue.KeyUsages & success) != success)
+ element.StatusFlags |= X509ChainStatusFlags.NotValidForUsage;
+ }
+
+ // 6.1.4.o - recognize and process other critical extension present in the certificate
+ ProcessCertificateExtensions (element);
+ }
+
+ private void WrapUp ()
+ {
+ X509ChainElement element = elements [0];
+ X509Certificate2 certificate = element.Certificate;
+
+ // 6.1.5.a - TODO if certificate n (our 0) wasn't self issued and explicit_policy != 0
+ if (IsSelfIssued (certificate)) {
+ // TODO... decrement explicit_policy by 1
+ }
+
+ // 6.1.5.b - TODO
+
+ // 6.1.5.c,d,e - not required by the X509Chain implementation
+
+ // 6.1.5.f - recognize and process other critical extension present in the certificate
+ ProcessCertificateExtensions (element);
+
+ // 6.1.5.g - TODO
+
+ // uncompressed the flags into several elements
+ for (int i = elements.Count - 1; i >= 0; i--) {
+ elements [i].UncompressFlags ();
+ }
+ }
+
+ private void ProcessCertificateExtensions (X509ChainElement element)
+ {
+ foreach (X509Extension ext in element.Certificate.Extensions) {
+ if (ext.Critical) {
+ switch (ext.Oid.Value) {
+ case "2.5.29.15": // X509KeyUsageExtension
+ case "2.5.29.19": // X509BasicConstraintsExtension
+ // we processed this extension
+ break;
+ default:
+ // note: Under Windows XP MS implementation seems to ignore
+ // certificate with unknown critical extensions.
+ element.StatusFlags |= X509ChainStatusFlags.InvalidExtension;
+ break;
+ }
+ }
+ }
+ }
+
+ private bool IsSignedWith (X509Certificate2 signed, AsymmetricAlgorithm pubkey)
+ {
+ if (pubkey == null)
+ return false;
+ // Sadly X509Certificate2 doesn't expose the signature nor the tbs (to be signed) structure
+ MX.X509Certificate mx = signed.MonoCertificate;
+ return (mx.VerifySignature (pubkey));
+ }
+
+ private string GetSubjectKeyIdentifier (X509Certificate2 certificate)
+ {
+ X509SubjectKeyIdentifierExtension ski = (certificate.Extensions["2.5.29.14"] as X509SubjectKeyIdentifierExtension);
+ return (ski == null) ? String.Empty : ski.SubjectKeyIdentifier;
+ }
+
+ // System.dll v2 doesn't have a class to deal with the AuthorityKeyIdentifier extension
+ static string GetAuthorityKeyIdentifier (X509Certificate2 certificate)
+ {
+ return GetAuthorityKeyIdentifier (certificate.MonoCertificate.Extensions ["2.5.29.35"]);
+ }
+
+ // but anyway System.dll v2 doesn't expose CRL in any way so...
+ static string GetAuthorityKeyIdentifier (MX.X509Crl crl)
+ {
+ return GetAuthorityKeyIdentifier (crl.Extensions ["2.5.29.35"]);
+ }
+
+ static string GetAuthorityKeyIdentifier (MX.X509Extension ext)
+ {
+ if (ext == null)
+ return String.Empty;
+ MX.Extensions.AuthorityKeyIdentifierExtension aki = new MX.Extensions.AuthorityKeyIdentifierExtension (ext);
+ byte[] id = aki.Identifier;
+ if (id == null)
+ return String.Empty;
+ StringBuilder sb = new StringBuilder ();
+ foreach (byte b in id)
+ sb.Append (b.ToString ("X02"));
+ return sb.ToString ();
+ }
+
+ // we check the revocation only once we have built the complete chain
+ private void CheckRevocationOnChain (X509ChainStatusFlags flag)
+ {
+ bool partial = ((flag & X509ChainStatusFlags.PartialChain) != 0);
+ bool online;
+
+ switch (ChainPolicy.RevocationMode) {
+ case X509RevocationMode.Online:
+ // default
+ online = true;
+ break;
+ case X509RevocationMode.Offline:
+ online = false;
+ break;
+ case X509RevocationMode.NoCheck:
+ return;
+ default:
+ throw new InvalidOperationException (Locale.GetText ("Invalid revocation mode."));
+ }
+
+ bool unknown = partial;
+ // from the root down to the end-entity
+ for (int i = elements.Count - 1; i >= 0; i--) {
+ bool check = true;
+
+ switch (ChainPolicy.RevocationFlag) {
+ case X509RevocationFlag.EndCertificateOnly:
+ check = (i == 0);
+ break;
+ case X509RevocationFlag.EntireChain:
+ check = true;
+ break;
+ case X509RevocationFlag.ExcludeRoot:
+ // default
+ check = (i != (elements.Count - 1));
+ // anyway, who's gonna sign that the root is invalid ?
+ break;
+ }
+
+ X509ChainElement element = elements [i];
+
+ // we can't assume the revocation status if the certificate is bad (e.g. invalid signature)
+ if (!unknown)
+ unknown |= ((element.StatusFlags & X509ChainStatusFlags.NotSignatureValid) != 0);
+
+ if (unknown) {
+ // we can skip the revocation checks as we can't be sure of them anyway
+ element.StatusFlags |= X509ChainStatusFlags.RevocationStatusUnknown;
+ element.StatusFlags |= X509ChainStatusFlags.OfflineRevocation;
+ } else if (check && !partial && !IsSelfIssued (element.Certificate)) {
+ // check for revocation (except for the trusted root and self-issued certs)
+ element.StatusFlags |= CheckRevocation (element.Certificate, i+1, online);
+ // if revoked, then all others following in the chain are unknown...
+ unknown |= ((element.StatusFlags & X509ChainStatusFlags.Revoked) != 0);
+ }
+ }
+ }
+
+ // This isn't how RFC3280 (section 6.3) deals with CRL, but then we don't (yet) support DP, deltas...
+ private X509ChainStatusFlags CheckRevocation (X509Certificate2 certificate, int ca, bool online)
+ {
+ X509ChainStatusFlags result = X509ChainStatusFlags.RevocationStatusUnknown;
+ X509ChainElement element = elements [ca];
+ X509Certificate2 ca_cert = element.Certificate;
+
+ // find the CRL from the "right" CA
+ while (IsSelfIssued (ca_cert) && (ca < elements.Count - 1)) {
+ // try with this self-issued
+ result = CheckRevocation (certificate, ca_cert, online);
+ if (result != X509ChainStatusFlags.RevocationStatusUnknown)
+ break;
+ ca++;
+ element = elements [ca];
+ ca_cert = element.Certificate;
+ }
+ if (result == X509ChainStatusFlags.RevocationStatusUnknown)
+ result = CheckRevocation (certificate, ca_cert, online);
+ return result;
+ }
+
+ private X509ChainStatusFlags CheckRevocation (X509Certificate2 certificate, X509Certificate2 ca_cert, bool online)
+ {
+ // change this if/when we support OCSP
+ X509KeyUsageExtension kue = (ca_cert.Extensions["2.5.29.15"] as X509KeyUsageExtension);
+ if (kue != null) {
+ // ... verify CrlSign is set
+ X509KeyUsageFlags success = X509KeyUsageFlags.CrlSign;
+ if ((kue.KeyUsages & success) != success) {
+ // FIXME - we should try to find an alternative CA that has the CrlSign bit
+ return X509ChainStatusFlags.RevocationStatusUnknown;
+ }
+ }
+
+ MX.X509Crl crl = FindCrl (ca_cert);
+
+ if ((crl == null) && online) {
+ // FIXME - download and install new CRL
+ // then you get a second chance
+ // crl = FindCrl (ca_cert, ref valid, ref out_of_date);
+
+ // We need to get the subjectAltName and an URI from there (or use OCSP)
+ // X509KeyUsageExtension subjectAltName = (ca_cert.Extensions["2.5.29.17"] as X509KeyUsageExtension);
+ }
+
+ if (crl != null) {
+ // validate the digital signature on the CRL using the CA public key
+ // note #1: we can't use X509Crl.VerifySignature(X509Certificate) because it duplicates
+ // checks and we loose the "why" of the failure
+ // note #2: we do this before other tests as an invalid signature could be a hacked CRL
+ // (so anything within can't be trusted)
+ if (!crl.VerifySignature (ca_cert.PublicKey.Key)) {
+ return X509ChainStatusFlags.RevocationStatusUnknown;
+ }
+
+ MX.X509Crl.X509CrlEntry entry = crl.GetCrlEntry (certificate.MonoCertificate);
+ if (entry != null) {
+ // We have an entry for this CRL that includes an unknown CRITICAL extension
+ // See [X.509 7.3] NOTE 4
+ if (!ProcessCrlEntryExtensions (entry))
+ return X509ChainStatusFlags.Revoked;
+
+ // FIXME - a little more is involved
+ if (entry.RevocationDate <= ChainPolicy.VerificationTime)
+ return X509ChainStatusFlags.Revoked;
+ }
+
+ // are we overdue for a CRL update ? if so we can't be sure of any certificate status
+ if (crl.NextUpdate < ChainPolicy.VerificationTime)
+ return X509ChainStatusFlags.RevocationStatusUnknown | X509ChainStatusFlags.OfflineRevocation;
+
+ // we have a CRL that includes an unknown CRITICAL extension
+ // we put this check at the end so we do not "hide" any Revoked flags
+ if (!ProcessCrlExtensions (crl)) {
+ return X509ChainStatusFlags.RevocationStatusUnknown;
+ }
+ } else {
+ return X509ChainStatusFlags.RevocationStatusUnknown;
+ }
+
+ return X509ChainStatusFlags.NoError;
+ }
+
+ static MX.X509Crl CheckCrls (string subject, string ski, MX.X509Store store)
+ {
+ if (store == null)
+ return null;
+
+ var crls = store.Crls;
+ foreach (MX.X509Crl crl in crls) {
+ if (crl.IssuerName == subject && (ski.Length == 0 || ski == GetAuthorityKeyIdentifier (crl)))
+ return crl;
+ }
+ return null; // No CRL found
+ }
+
+ private MX.X509Crl FindCrl (X509Certificate2 caCertificate)
+ {
+ string subject = caCertificate.SubjectName.Decode (X500DistinguishedNameFlags.None);
+ string ski = GetSubjectKeyIdentifier (caCertificate);
+
+ // consider that the LocalMachine directories could not exists... and cannot be created by the user
+ MX.X509Crl result = CheckCrls (subject, ski, LMCAStore.Store);
+ if (result != null)
+ return result;
+ if (location == StoreLocation.CurrentUser) {
+ result = CheckCrls (subject, ski, UserCAStore.Store);
+ if (result != null)
+ return result;
+ }
+
+ // consider that the LocalMachine directories could not exists... and cannot be created by the user
+ result = CheckCrls (subject, ski, LMRootStore.Store);
+ if (result != null)
+ return result;
+ if (location == StoreLocation.CurrentUser) {
+ result = CheckCrls (subject, ski, UserRootStore.Store);
+ if (result != null)
+ return result;
+ }
+ return null;
+ }
+
+ private bool ProcessCrlExtensions (MX.X509Crl crl)
+ {
+ foreach (MX.X509Extension ext in crl.Extensions) {
+ if (ext.Critical) {
+ switch (ext.Oid) {
+ case "2.5.29.20": // cRLNumber
+ case "2.5.29.35": // authorityKeyIdentifier
+ // we processed/know about this extension
+ break;
+ default:
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private bool ProcessCrlEntryExtensions (MX.X509Crl.X509CrlEntry entry)
+ {
+ foreach (MX.X509Extension ext in entry.Extensions) {
+ if (ext.Critical) {
+ switch (ext.Oid) {
+ case "2.5.29.21": // cRLReason
+ // we processed/know about this extension
+ break;
+ default:
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// X509Helper2.cs
+//
+// Authors:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (C) 2016 Xamarin, Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if SECURITY_DEP
+namespace System.Security.Cryptography.X509Certificates
+{
+ internal static class X509Helper2
+ {
+ internal static void ThrowIfContextInvalid (X509CertificateImpl impl)
+ {
+ X509Helper.ThrowIfContextInvalid (impl);
+ }
+
+ internal static X509Certificate2Impl Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
+ {
+ var impl = new X509Certificate2ImplMono ();
+ impl.Import (rawData, password, keyStorageFlags);
+ return impl;
+ }
+
+ internal static X509Certificate2Impl Import (X509Certificate cert)
+ {
+ var impl2 = cert.Impl as X509Certificate2Impl;
+ if (impl2 != null)
+ return (X509Certificate2Impl)impl2.Clone ();
+ return Import (cert.GetRawCertData (), null, X509KeyStorageFlags.DefaultKeySet);
+ }
+
+ internal static X509ChainImpl CreateChainImpl (bool useMachineContext)
+ {
+ return new X509ChainImplMono (useMachineContext);
+ }
+
+ public static bool IsValid (X509ChainImpl impl)
+ {
+ return impl != null && impl.IsValid;
+ }
+
+ internal static void ThrowIfContextInvalid (X509ChainImpl impl)
+ {
+ if (!IsValid (impl))
+ throw GetInvalidChainContextException ();
+ }
+
+ internal static Exception GetInvalidChainContextException ()
+ {
+ return new CryptographicException (Locale.GetText ("Chain instance is empty."));
+ }
+ }
+}
+#endif
namespace System.Security.Cryptography.X509Certificates {
- public sealed class X509Store : IDisposable {
+ public sealed class X509Store {
private string _name;
private StoreLocation _location;
_location = storeLocation;
}
- public void Dispose ()
- {
- }
-
// properties
public X509Certificate2Collection Certificates {
System.Security.Cryptography.X509Certificates/X509Certificate2Collection.cs
System.Security.Cryptography.X509Certificates/X509Certificate2.cs
System.Security.Cryptography.X509Certificates/X509Certificate2Enumerator.cs
+System.Security.Cryptography.X509Certificates/X509Certificate2Impl.cs
+System.Security.Cryptography.X509Certificates/X509Certificate2ImplMono.cs
System.Security.Cryptography.X509Certificates/X509CertificateCollection.cs
System.Security.Cryptography.X509Certificates/X509Chain.cs
System.Security.Cryptography.X509Certificates/X509ChainElementCollection.cs
System.Security.Cryptography.X509Certificates/X509ChainElement.cs
System.Security.Cryptography.X509Certificates/X509ChainElementEnumerator.cs
+System.Security.Cryptography.X509Certificates/X509ChainImpl.cs
+System.Security.Cryptography.X509Certificates/X509ChainImplMono.cs
System.Security.Cryptography.X509Certificates/X509ChainPolicy.cs
System.Security.Cryptography.X509Certificates/X509ChainStatus.cs
System.Security.Cryptography.X509Certificates/X509ChainStatusFlags.cs
System.Security.Cryptography.X509Certificates/X509ExtensionEnumerator.cs
System.Security.Cryptography.X509Certificates/X509FindType.cs
System.Security.Cryptography.X509Certificates/X509IncludeOption.cs
+System.Security.Cryptography.X509Certificates/X509Helper2.cs
System.Security.Cryptography.X509Certificates/X509KeyUsageExtension.cs
System.Security.Cryptography.X509Certificates/X509KeyUsageFlags.cs
System.Security.Cryptography.X509Certificates/X509NameType.cs
public static MyNetworkStream CreateNS (int port)
{
- return CreateNS (port, 5000);
+ return CreateNS (IPAddress.Loopback, port, 5000);
}
public static MyNetworkStream CreateNS (int port, int timeout_ms)
+ {
+ return CreateNS (IPAddress.Loopback, port, timeout_ms);
+ }
+
+ public static MyNetworkStream CreateNS (IPAddress ip, int port)
+ {
+ return CreateNS (ip, port, 5000);
+ }
+
+ public static MyNetworkStream CreateNS (IPAddress ip, int port, int timeout_ms)
{
Socket sock = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- sock.Connect (new IPEndPoint (IPAddress.Loopback, port));
+ sock.Connect (new IPEndPoint (ip, port));
sock.SendTimeout = timeout_ms;
sock.ReceiveTimeout = timeout_ms;
return new MyNetworkStream (sock);
using System.Net;
using System.Net.Sockets;
using System.Text;
+using System.Collections.Generic;
using NUnit.Framework;
listener.Close ();
}
+ [Test]
+ public void HttpRequestIsLocal ()
+ {
+ var ips = new List<IPAddress> (Dns.GetHostAddresses (Dns.GetHostName ()));
+ ips.Add (IPAddress.Loopback);
+ foreach (var ip in ips) {
+ if (ip.AddressFamily != AddressFamily.InterNetwork)
+ continue;
+
+ HttpListener listener = HttpListener2Test.CreateAndStartListener (
+ "http://" + ip + ":9000/HttpRequestIsLocal/");
+ NetworkStream ns = HttpListener2Test.CreateNS (ip, 9000);
+ HttpListener2Test.Send (ns, "GET /HttpRequestIsLocal/ HTTP/1.0\r\n\r\n");
+ HttpListenerContext ctx = listener.GetContext ();
+ HttpListenerRequest request = ctx.Request;
+ Assert.AreEqual (true, request.IsLocal, "IP " + ip + " is not local");
+ listener.Close ();
+ }
+ }
+
[Test] // #29927
public void HttpRequestUriUnescape ()
{
}
[Test]
- [ExpectedException (typeof (ArgumentException))]
public void GetNameInfo_Invalid_True ()
{
- new X509Certificate2 ().GetNameInfo ((X509NameType) Int32.MinValue, true);
+ try {
+ // MS throws ArgumentException, Mono's new implementation throws
+ // CryptographicException, which is consistent with other "certificate
+ // instance is empty" situations.
+ new X509Certificate2 ().GetNameInfo ((X509NameType) Int32.MinValue, true);
+ Assert.Fail ("Expected exception.");
+ } catch (ArgumentException) {
+ } catch (CryptographicException) {
+ } catch (Exception ex) {
+ Assert.Fail ("Expected 'ArgumentException' or 'CryptographicException', got '{0}'", ex.GetType ());
+ }
}
[Test]
- [ExpectedException (typeof (ArgumentException))]
public void GetNameInfo_Invalid_False ()
{
- new X509Certificate2 ().GetNameInfo ((X509NameType) Int32.MinValue, false);
+ try {
+ // MS throws ArgumentException, Mono's new implementation throws
+ // CryptographicException, which is consistent with other "certificate
+ // instance is empty" situations.
+ new X509Certificate2 ().GetNameInfo ((X509NameType) Int32.MinValue, false);
+ Assert.Fail ("Expected exception.");
+ } catch (ArgumentException) {
+ } catch (CryptographicException) {
+ } catch (Exception ex) {
+ Assert.Fail ("Expected 'ArgumentException' or 'CryptographicException', got '{0}'", ex.GetType ());
+ }
}
[Test]
}
[Test]
- [ExpectedException (typeof (NullReferenceException))]
public void Empty_GetNameInfo_DnsName ()
{
- new X509Certificate2 ().GetNameInfo (X509NameType.DnsName, true);
+ try {
+ // MS throws NullReferenceException, Mono's new implementation throws
+ // CryptographicException, which is consistent with other "certificate
+ // instance is empty" situations.
+ new X509Certificate2 ().GetNameInfo (X509NameType.DnsName, true);
+ Assert.Fail ("Expected exception.");
+ } catch (NullReferenceException) {
+ } catch (CryptographicException) {
+ } catch (Exception ex) {
+ Assert.Fail ("Expected 'NullReferenceException' or 'CryptographicException', got '{0}'", ex.GetType ());
+ }
}
[Test]
System.Security.Cryptography.X509Certificates/X509Certificate2.cs
System.Security.Cryptography.X509Certificates/X509Certificate2Collection.cs
System.Security.Cryptography.X509Certificates/X509Certificate2Enumerator.cs
+System.Security.Cryptography.X509Certificates/X509Certificate2Impl.cs
+System.Security.Cryptography.X509Certificates/X509Certificate2ImplMono.cs
System.Security.Cryptography.X509Certificates/X509CertificateCollection.cs
System.Security.Cryptography.X509Certificates/X509Chain.cs
System.Security.Cryptography.X509Certificates/X509ChainElement.cs
System.Security.Cryptography.X509Certificates/X509ChainElementCollection.cs
System.Security.Cryptography.X509Certificates/X509ChainElementEnumerator.cs
+System.Security.Cryptography.X509Certificates/X509ChainImpl.cs
+System.Security.Cryptography.X509Certificates/X509ChainImplMono.cs
System.Security.Cryptography.X509Certificates/X509ChainPolicy.cs
System.Security.Cryptography.X509Certificates/X509ChainStatus.cs
System.Security.Cryptography.X509Certificates/X509ChainStatusFlags.cs
System.Security.Cryptography.X509Certificates/X509ExtensionEnumerator.cs
System.Security.Cryptography.X509Certificates/X509FindType.cs
System.Security.Cryptography.X509Certificates/X509IncludeOption.cs
+System.Security.Cryptography.X509Certificates/X509Helper2.cs
System.Security.Cryptography.X509Certificates/X509KeyUsageExtension.cs
System.Security.Cryptography.X509Certificates/X509KeyUsageFlags.cs
System.Security.Cryptography.X509Certificates/X509NameType.cs
public sealed class FileInfo : FileSystemInfo
{
private bool exists;
+ private string displayPath;
public FileInfo (string fileName)
{
OriginalPath = fileName;
FullPath = Path.GetFullPath (fileName);
+
+ displayPath = OriginalPath;
}
private FileInfo (SerializationInfo info, StreamingContext context)
: base (info, context)
{
+ displayPath = OriginalPath;
}
internal override void InternalRefresh ()
File.Move (FullPath, destFullPath);
this.FullPath = destFullPath;
+
+ displayPath = destFileName;
}
public FileInfo CopyTo (string destFileName)
public override string ToString ()
{
- return OriginalPath;
+ return displayPath;
}
#if !MOBILE
impl = X509Helper.InitFromHandle (handle);
}
+ internal X509Certificate (X509CertificateImpl impl)
+ {
+ if (impl == null)
+ throw new ArgumentNullException ("impl");
+
+ this.impl = X509Helper.InitFromCertificate (impl);
+ }
+
public X509Certificate (System.Security.Cryptography.X509Certificates.X509Certificate cert)
{
if (cert == null)
hideDates = false;
}
+ internal void ImportHandle (X509CertificateImpl impl)
+ {
+ Reset ();
+ this.impl = impl;
+ }
+
+ internal X509CertificateImpl Impl {
+ get {
+ X509Helper.ThrowIfContextInvalid (impl);
+ return impl;
+ }
+ }
+
+ internal bool IsValid {
+ get { return X509Helper.IsValid (impl); }
+ }
+
+ internal void ThrowIfContextInvalid ()
+ {
+ X509Helper.ThrowIfContextInvalid (impl);
+ }
// public methods
return null;
X509Helper.ThrowIfContextInvalid (impl);
- return impl.GetEffectiveDateString ().ToString ();
+ return impl.GetValidFrom ().ToLocalTime ().ToString ();
}
// strangly there are no DateTime returning function
return null;
X509Helper.ThrowIfContextInvalid (impl);
- return impl.GetExpirationDateString ().ToString ();
+ return impl.GetValidUntil ().ToLocalTime ().ToString ();
}
// well maybe someday there'll be support for PGP or SPKI ?
public abstract X509CertificateImpl Clone ();
- public abstract string GetSubjectSummary ();
-
public abstract string GetIssuerName (bool legacyV1Mode);
public abstract string GetSubjectName (bool legacyV1Mode);
public abstract byte[] GetRawCertData ();
- public abstract DateTime GetEffectiveDateString ();
+ public abstract DateTime GetValidFrom ();
- public abstract DateTime GetExpirationDateString ();
+ public abstract DateTime GetValidUntil ();
byte[] cachedCertificateHash;
return MX.X501.ToString (x509.GetIssuerName (), true, ", ", true);
}
- public override string GetSubjectSummary ()
- {
- ThrowIfContextInvalid ();
- return x509.SubjectName;
- }
-
public override string GetSubjectName (bool legacyV1Mode)
{
ThrowIfContextInvalid ();
return sha.ComputeHash (x509.RawData);
}
- public override DateTime GetEffectiveDateString ()
+ public override DateTime GetValidFrom ()
{
ThrowIfContextInvalid ();
- return x509.ValidFrom.ToLocalTime ();
+ return x509.ValidFrom;
}
- public override DateTime GetExpirationDateString ()
+ public override DateTime GetValidUntil ()
{
ThrowIfContextInvalid ();
- return x509.ValidUntil.ToLocalTime ();
+ return x509.ValidUntil;
}
public override bool Equals (X509CertificateImpl other, out bool result)
StringBuilder sb = new StringBuilder ();
sb.AppendFormat ("[Subject]{0} {1}{0}{0}", nl, GetSubjectName (false));
sb.AppendFormat ("[Issuer]{0} {1}{0}{0}", nl, GetIssuerName (false));
- sb.AppendFormat ("[Not Before]{0} {1}{0}{0}", nl, GetEffectiveDateString ());
- sb.AppendFormat ("[Not After]{0} {1}{0}{0}", nl, GetExpirationDateString ());
+ sb.AppendFormat ("[Not Before]{0} {1}{0}{0}", nl, GetValidFrom ().ToLocalTime ());
+ sb.AppendFormat ("[Not After]{0} {1}{0}{0}", nl, GetValidUntil ().ToLocalTime ());
sb.AppendFormat ("[Thumbprint]{0} {1}{0}", nl, X509Helper.ToHexString (GetCertHash ()));
sb.Append (nl);
return sb.ToString ();
}
}
+ [Test] //Covers #38796
+ public void ToStringAfterMoveTo ()
+ {
+ string name1 = "FIT.ToStringAfterMoveTo.Test";
+ string name2 = "FIT.ToStringAfterMoveTo.Test.Alt";
+ string path1 = TempFolder + DSC + name1;
+ string path2 = TempFolder + DSC + name2;
+ DeleteFile (path1);
+ DeleteFile (path2);
+
+ try {
+ File.Create (path1).Close ();
+ FileInfo info = new FileInfo (path1);
+ Assert.AreEqual (path1, info.ToString (), "#A");
+
+ info.MoveTo (path2);
+ Assert.AreEqual (path2, info.ToString (), "#B");
+ } finally {
+ DeleteFile (path1);
+ DeleteFile (path2);
+ }
+ }
+
#if !MOBILE
[Test]
public void Replace1 ()
return IsLeftResolvedExpressionValid (dmb.Arguments [0].Expr);
}
- if (expr is ConstantExpr || expr is TypeExpr || expr is NamespaceExpression || expr is This)
+ if (expr is ConstantExpr || expr is TypeExpr || expr is NamespaceExpression || expr is VariableReference)
return true;
return false;
return ParseResult.Success;
// csc options that we don't support
- case "/utf8output":
- case "/subsystemversion":
+ case "/analyzer":
+ case "/appconfig":
+ case "/baseaddress":
+ case "/deterministic":
+ case "/errorendlocation":
+ case "/errorlog":
+ case "/features":
case "/highentropyva":
case "/highentropyva+":
case "/highentropyva-":
- case "/win32manifest":
+ case "/link":
+ case "/moduleassemblyname":
case "/nowin32manifest":
+ case "/pathmap":
+ case "/pdb":
+ case "/preferreduilang":
+ case "/publicsign":
+ case "/reportanalyzer":
+ case "/ruleset":
+ case "/sqmsessionguid":
+ case "/subsystemversion":
+ case "/utf8output":
+ case "/win32manifest":
return ParseResult.Success;
default:
public static event Action Act = null;
public static dynamic BBB = null;
+ void ParameterTest (Person ParPerson)
+ {
+ Console.WriteLine (nameof (ParPerson.MyCar.Year));
+ }
+
public static int Main ()
{
string name;
if (name != "ToString")
return 7;
+ Person LocPerson = null;
+ name = nameof (LocPerson.MyCar.Year);
+ if (name != "Year")
+ return 8;
+
return 0;
}
}
<size>40</size>
</method>
<method name="Int32 Main()" attrs="150">
- <size>213</size>
+ <size>244</size>
</method>
<method name="Void .ctor()" attrs="6278">
<size>7</size>
<size>7</size>
</method>
</type>
+ <type name="MainClass">
+ <method name="Void ParameterTest(Person)" attrs="129">
+ <size>12</size>
+ </method>
+ </type>
</test>
<test name="test-null-operator-01.cs">
<type name="S">
PROGRAM = cil-stringreplacer.exe
NO_INSTALL = yes
-LOCAL_MCS_FLAGS = -r:System.dll
+LOCAL_MCS_FLAGS = -r:System.dll -r:Mono.Cecil.dll
include ../../build/executable.make
static void RewriteAssembly (string assemblyLocation, Dictionary<string, string> resourcesStrings, CmdOptions options)
{
- var assembly = AssemblyDefinition.ReadAssembly (assemblyLocation);
+ var readerParameters = new ReaderParameters { ReadSymbols = true };
+ var assembly = AssemblyDefinition.ReadAssembly (assemblyLocation, readerParameters);
foreach (var module in assembly.Modules) {
foreach (var type in module.GetTypes ()) {
foreach (var method in type.Methods) {
}
}
- assembly.Write (assemblyLocation);
+ var writerParameters = new WriterParameters { WriteSymbols = true };
+ assembly.Write (assemblyLocation, writerParameters);
}
static bool LoadGetResourceStrings (Dictionary<string, string> resourcesStrings, CmdOptions options)
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+ <PropertyGroup>\r
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>\r
+ <Platform Condition=" '$(Platform)' == '' ">x86</Platform>\r
+ <ProjectGuid>{7A08A2B6-5AC3-4268-8566-B07648B50D01}</ProjectGuid>\r
+ <OutputType>Exe</OutputType>\r
+ <RootNamespace>cilstringreplacer</RootNamespace>\r
+ <AssemblyName>cilstringreplacer</AssemblyName>\r
+ <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">\r
+ <DebugSymbols>true</DebugSymbols>\r
+ <DebugType>full</DebugType>\r
+ <Optimize>false</Optimize>\r
+ <OutputPath>bin\Debug</OutputPath>\r
+ <DefineConstants>DEBUG;</DefineConstants>\r
+ <ErrorReport>prompt</ErrorReport>\r
+ <WarningLevel>4</WarningLevel>\r
+ <ExternalConsole>false</ExternalConsole>\r
+ <PlatformTarget>x86</PlatformTarget>\r
+ <Commandlineparameters>--resourcestrings:/Users/marek/git/mono/external/referencesource/mscorlib/mscorlib.txt /Users/marek/git/mono/mcs/class/lib/net_4_x/mscorlib.dll</Commandlineparameters>\r
+ <ConsolePause>false</ConsolePause>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">\r
+ <Optimize>true</Optimize>\r
+ <OutputPath>bin\Release</OutputPath>\r
+ <ErrorReport>prompt</ErrorReport>\r
+ <WarningLevel>4</WarningLevel>\r
+ <ExternalConsole>true</ExternalConsole>\r
+ <PlatformTarget>x86</PlatformTarget>\r
+ </PropertyGroup>\r
+ <ItemGroup>\r
+ <Reference Include="System" />\r
+ <Reference Include="Mono.Cecil">\r
+ <HintPath>..\..\class\lib\net_4_x\Mono.Cecil.dll</HintPath>\r
+ </Reference>\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <Compile Include="cil-stringreplacer.cs">\r
+ <Link>cil-stringreplacer.cs</Link>\r
+ </Compile>\r
+ <Compile Include="..\..\class\Mono.Options\Mono.Options\Options.cs">\r
+ <Link>Options.cs</Link>\r
+ </Compile>\r
+ </ItemGroup>\r
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />\r
+</Project>
\ No newline at end of file
cil-stringreplacer.cs
../../class/Mono.Options/Mono.Options/Options.cs
-../../../external/cecil/Mono.Cecil/*.cs
-../../../external/cecil/Mono.Cecil.Cil/*.cs
-../../../external/cecil/Mono.Cecil.Metadata/*.cs
-../../../external/cecil/Mono.Cecil.PE/*.cs
-../../../external/cecil/Mono.Collections.Generic/*.cs
-../../../external/cecil/Mono/Empty.cs
-../../../external/cecil/Mono.Security.Cryptography/*.cs
SUBDIRS =
include ../../build/rules.make
-ALL_PROGRAMS = mono-api-info.exe
-
-COMMON_SOURCES = \
- AssemblyResolver.cs \
- Util.cs \
- WellFormedXmlWriter.cs
+ALL_PROGRAMS = mono-api-info.exe mono-api-html.exe
PROGRAM_INSTALL_DIR = $(mono_libdir)/mono/$(FRAMEWORK_VERSION)
APIINFO_SOURCES = \
mono-api-info.cs \
- ../../class/Mono.Options/Mono.Options/Options.cs \
- $(COMMON_SOURCES)
+ AssemblyResolver.cs \
+ Util.cs \
+ WellFormedXmlWriter.cs \
+ ../../class/Mono.Options/Mono.Options/Options.cs
+
+APIHTML_SOURCES = \
+ mono-api-html/ApiChange.cs \
+ mono-api-html/ApiDiff.cs \
+ mono-api-html/AssemblyComparer.cs \
+ mono-api-html/ClassComparer.cs \
+ mono-api-html/Comparer.cs \
+ mono-api-html/ConstructorComparer.cs \
+ mono-api-html/EventComparer.cs \
+ mono-api-html/FieldComparer.cs \
+ mono-api-html/Helpers.cs \
+ mono-api-html/InterfaceComparer.cs \
+ mono-api-html/MemberComparer.cs \
+ mono-api-html/MethodComparer.cs \
+ mono-api-html/NamespaceComparer.cs \
+ mono-api-html/PropertyComparer.cs \
+ ../../class/Mono.Options/Mono.Options/Options.cs
-DISTFILES= $(COMMON_SOURCES) $(APIINFO_SOURCES)
+
+DISTFILES= $(APIINFO_SOURCES) $(APIHTML_SOURCES)
all-local: $(ALL_PROGRAMS)
dist-local: dist-default
-mono-api-info.exe: $(APIINFO_SOURCES) ../../class/Mono.Options/Mono.Options/Options.cs
+mono-api-info.exe: $(APIINFO_SOURCES)
$(CSCOMPILE) -r:Mono.Cecil.dll -r:System.Xml.dll -r:System.Core.dll -r:System.dll -out:$@ $^
+
+mono-api-html.exe: $(APIHTML_SOURCES)
+ $(CSCOMPILE) -r:Mono.Cecil.dll -r:System.Xml.dll -r:System.Core.dll -r:System.dll -r:System.Xml.Linq.dll -out:$@ $^
Copy (name, asmb_path, true);
+ var name_pdb = Path.ChangeExtension (name, ".pdb");
+ if (File.Exists (name_pdb)) {
+ Copy (name_pdb, Path.ChangeExtension (asmb_path, ".pdb"), true);
+ }
+
foreach (string ext in siblings) {
string sibling = String.Concat (name, ext);
if (File.Exists (sibling))
string pkg_path = AbsoluteToRelativePath (ref_dir, pkg_path_abs);
symlink (pkg_path, ref_path);
+ var pdb_pkg_path = Path.ChangeExtension (pkg_path, ".pdb");
+ var pdb_ref_path = Path.ChangeExtension (ref_path, ".pdb");
+
+ if (File.Exists (pdb_pkg_path)) {
+ symlink (pdb_pkg_path, pdb_ref_path);
+ } else {
+ try {
+ File.Delete (pdb_ref_path);
+ } catch {
+ // Ignore error, just delete files that should not be there.
+ }
+ }
+
foreach (string ext in siblings) {
string sibling = String.Concat (pkg_path, ext);
string sref = String.Concat (ref_path, ext);
+
if (File.Exists (sibling))
symlink (sibling, sref);
else {
mono_install_assembly_postload_search_hook ((MonoAssemblySearchFunc)mono_domain_assembly_postload_search, GUINT_TO_POINTER (FALSE));
mono_install_assembly_postload_refonly_search_hook ((MonoAssemblySearchFunc)mono_domain_assembly_postload_search, GUINT_TO_POINTER (TRUE));
mono_install_assembly_load_hook (mono_domain_fire_assembly_load, NULL);
- mono_install_lookup_dynamic_token (mono_reflection_lookup_dynamic_token);
mono_thread_init (start_cb, attach_cb);
if (!mono_error_ok (&error)) {
mono_error_cleanup (&error);
g_free (dir_name);
- mono_error_set_generic_error (oerror, "System", "ExecutionEngineException", "Failed to create shadow copy (invalid characters in shadow directory name).");
+ mono_error_set_execution_engine (oerror, "Failed to create shadow copy (invalid characters in shadow directory name).");
return NULL;
}
shadow = get_shadow_assembly_location (filename, &error);
if (!mono_error_ok (&error)) {
mono_error_cleanup (&error);
- mono_error_set_generic_error (oerror, "System", "ExecutionEngineException", "Failed to create shadow copy (invalid characters in file name).");
+ mono_error_set_execution_engine (oerror, "Failed to create shadow copy (invalid characters in file name).");
return NULL;
}
if (ensure_directory_exists (shadow) == FALSE) {
g_free (shadow);
- mono_error_set_generic_error (oerror, "System", "ExecutionEngineException", "Failed to create shadow copy (ensure directory exists).");
+ mono_error_set_execution_engine (oerror, "Failed to create shadow copy (ensure directory exists).");
return NULL;
}
if (GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_PATH_NOT_FOUND)
return NULL; /* file not found, shadow copy failed */
- mono_error_set_generic_error (oerror, "System", "ExecutionEngineException", "Failed to create shadow copy (CopyFile).");
+ mono_error_set_execution_engine (oerror, "Failed to create shadow copy (CopyFile).");
return NULL;
}
if (copy_result == FALSE) {
g_free (shadow);
- mono_error_set_generic_error (oerror, "System", "ExecutionEngineException", "Failed to create shadow copy of sibling data (CopyFile).");
+ mono_error_set_execution_engine (oerror, "Failed to create shadow copy of sibling data (CopyFile).");
return NULL;
}
/* Create a .ini file containing the original assembly location */
if (!shadow_copy_create_ini (shadow, filename)) {
g_free (shadow);
- mono_error_set_generic_error (oerror, "System", "ExecutionEngineException", "Failed to create shadow copy .ini file.");
+ mono_error_set_execution_engine (oerror, "Failed to create shadow copy .ini file.");
return NULL;
}
void
ves_icall_System_AppDomain_InternalUnload (gint32 domain_id)
{
+ MonoException *exc = NULL;
MonoDomain * domain = mono_domain_get_by_id (domain_id);
if (NULL == domain) {
- MonoException *exc = mono_get_exception_execution_engine ("Failed to unload domain, domain id not found");
+ mono_get_exception_execution_engine ("Failed to unload domain, domain id not found");
mono_set_pending_exception (exc);
return;
}
return;
#endif
- mono_domain_unload (domain);
+ mono_domain_try_unload (domain, (MonoObject**)&exc);
+ if (exc)
+ mono_set_pending_exception (exc);
}
gboolean
{
MonoObject *exc = NULL;
mono_domain_try_unload (domain, &exc);
- if (exc)
- mono_raise_exception ((MonoException*)exc);
}
static guint32
MONO_API void
mono_domain_set_internal (MonoDomain *domain);
+MONO_RT_EXTERNAL_ONLY
MONO_API void
mono_domain_unload (MonoDomain *domain);
typedef gpointer (*MonoRemotingTrampoline) (MonoDomain *domain, MonoMethod *method, MonoRemotingTarget target);
typedef gpointer (*MonoDelegateTrampoline) (MonoDomain *domain, MonoClass *klass);
-typedef gpointer (*MonoLookupDynamicToken) (MonoImage *image, guint32 token, gboolean valid_token, MonoClass **handle_class, MonoGenericContext *context);
-
typedef gboolean (*MonoGetCachedClassInfo) (MonoClass *klass, MonoCachedClassInfo *res);
typedef gboolean (*MonoGetClassFromName) (MonoImage *image, const char *name_space, const char *name, MonoClass **res);
mono_classes_cleanup (void);
void
-mono_class_layout_fields (MonoClass *klass);
+mono_class_layout_fields (MonoClass *klass, int instance_size);
void
mono_class_setup_interface_offsets (MonoClass *klass);
mono_install_delegate_trampoline (MonoDelegateTrampoline func);
gpointer
-mono_lookup_dynamic_token (MonoImage *image, guint32 token, MonoGenericContext *context);
+mono_lookup_dynamic_token (MonoImage *image, guint32 token, MonoGenericContext *context, MonoError *error);
gpointer
-mono_lookup_dynamic_token_class (MonoImage *image, guint32 token, gboolean check_token, MonoClass **handle_class, MonoGenericContext *context);
-
-void
-mono_install_lookup_dynamic_token (MonoLookupDynamicToken func);
+mono_lookup_dynamic_token_class (MonoImage *image, guint32 token, gboolean check_token, MonoClass **handle_class, MonoGenericContext *context, MonoError *error);
gpointer
mono_runtime_create_jump_trampoline (MonoDomain *domain, MonoMethod *method, gboolean add_sync_wrapper);
int i, blittable = TRUE;
guint32 real_size = 0;
guint32 packing_size = 0;
+ int instance_size;
gboolean explicit_size;
MonoClassField *field;
MonoGenericContainer *container = NULL;
}
}
- klass->instance_size = 0;
+ instance_size = 0;
if (!klass->rank)
klass->sizes.class_size = 0;
return;
}
}
- klass->instance_size += klass->parent->instance_size;
+ instance_size += klass->parent->instance_size;
klass->min_align = klass->parent->min_align;
/* we use |= since it may have been set already */
klass->has_references |= klass->parent->has_references;
blittable = klass->parent->blittable;
} else {
- klass->instance_size = sizeof (MonoObject);
+ instance_size = sizeof (MonoObject);
klass->min_align = 1;
}
return;
}
klass->packing_size = packing_size;
- real_size += klass->instance_size;
+ real_size += instance_size;
}
if (!top) {
if (explicit_size && real_size) {
- klass->instance_size = MAX (real_size, klass->instance_size);
+ instance_size = MAX (real_size, instance_size);
}
klass->blittable = blittable;
+ if (!klass->instance_size)
+ klass->instance_size = instance_size;
mono_memory_barrier ();
klass->size_inited = 1;
klass->fields_inited = 1;
return;
}
if (explicit_size && real_size) {
- klass->instance_size = MAX (real_size, klass->instance_size);
+ instance_size = MAX (real_size, instance_size);
}
if (mono_class_has_failure (klass))
return;
- mono_class_layout_fields (klass);
+ mono_class_layout_fields (klass, instance_size);
/*valuetypes can't be neither bigger than 1Mb or empty. */
if (klass->valuetype && (klass->instance_size <= 0 || klass->instance_size > (0x100000 + sizeof (MonoObject))))
/*
* mono_class_layout_fields:
* @class: a class
+ * @instance_size: base instance size
*
* Compute the placement of fields inside an object or struct, according to
* the layout rules and set the following fields in @class:
* LOCKING: this is supposed to be called with the loader lock held.
*/
void
-mono_class_layout_fields (MonoClass *klass)
+mono_class_layout_fields (MonoClass *klass, int instance_size)
{
int i;
const int top = klass->field.count;
/*
* Compute field layout and total size (not considering static fields)
*/
-
switch (layout) {
case TYPE_ATTRIBUTE_AUTO_LAYOUT:
case TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT:
real_size = field->offset + size;
}
- klass->instance_size = MAX (real_size, klass->instance_size);
+ instance_size = MAX (real_size, instance_size);
- if (klass->instance_size & (klass->min_align - 1)) {
- klass->instance_size += klass->min_align - 1;
- klass->instance_size &= ~(klass->min_align - 1);
+ if (instance_size & (klass->min_align - 1)) {
+ instance_size += klass->min_align - 1;
+ instance_size &= ~(klass->min_align - 1);
}
}
break;
g_free (ref_bitmap);
}
- klass->instance_size = MAX (real_size, klass->instance_size);
- if (klass->instance_size & (klass->min_align - 1)) {
- klass->instance_size += klass->min_align - 1;
- klass->instance_size &= ~(klass->min_align - 1);
+ instance_size = MAX (real_size, instance_size);
+ if (instance_size & (klass->min_align - 1)) {
+ instance_size += klass->min_align - 1;
+ instance_size &= ~(klass->min_align - 1);
}
break;
}
* unaligned accesses otherwise. See #78990 for a testcase.
*/
if (mono_align_small_structs) {
- if (klass->instance_size <= sizeof (MonoObject) + sizeof (gpointer))
- klass->min_align = MAX (klass->min_align, klass->instance_size - sizeof (MonoObject));
+ if (instance_size <= sizeof (MonoObject) + sizeof (gpointer))
+ klass->min_align = MAX (klass->min_align, instance_size - sizeof (MonoObject));
}
}
+ if (klass->instance_size && !klass->image->dynamic) {
+ /* Might be already set using cached info */
+ g_assert (klass->instance_size == instance_size);
+ } else {
+ klass->instance_size = instance_size;
+ }
mono_memory_barrier ();
klass->size_inited = 1;
static void
mono_class_setup_vtable_full (MonoClass *klass, GList *in_setup)
{
+ MonoError error;
MonoMethod **overrides;
MonoGenericContext *context;
guint32 type_token;
* This is true since we don't do layout all over again for them, we simply inflate
* the layout of the parent.
*/
- mono_reflection_get_dynamic_overrides (klass, &overrides, &onum);
+ mono_reflection_get_dynamic_overrides (klass, &overrides, &onum, &error);
+ if (!is_ok (&error)) {
+ mono_loader_unlock ();
+ g_list_remove (in_setup, klass);
+ mono_class_set_failure (klass, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf("Could not load list of method overrides due to %s", mono_error_get_message (&error)));
+ mono_error_cleanup (&error);
+ return;
+ }
} else {
/* The following call fails if there are missing methods in the type */
/* FIXME it's probably a good idea to avoid this for generic instances. */
mono_error_set_bad_image (error, image,"Bad token table for dynamic image: %x", table);
return NULL;
}
- klass = (MonoClass *)mono_lookup_dynamic_token (image, type_token, NULL); /*FIXME proper error handling*/
+ klass = (MonoClass *)mono_lookup_dynamic_token (image, type_token, NULL, error);
goto done;
}
mono_error_init (error);
//FIXME: this will not fix the very issue for which mono_type_get_full exists -but how to do it then?
- if (image_is_dynamic (image))
- return mono_class_get_type ((MonoClass *)mono_lookup_dynamic_token (image, type_token, context));
+ if (image_is_dynamic (image)) {
+ MonoClass *klass = (MonoClass *)mono_lookup_dynamic_token (image, type_token, context, error);
+ return_val_if_nok (error, NULL);
+ return mono_class_get_type (klass);
+ }
if ((type_token & 0xff000000) != MONO_TOKEN_TYPE_SPEC) {
MonoClass *klass = mono_class_get_checked (image, type_token, error);
gboolean
mono_class_is_assignable_from (MonoClass *klass, MonoClass *oklass)
{
+ MonoError error;
/*FIXME this will cause a lot of irrelevant stuff to be loaded.*/
if (!klass->inited)
mono_class_init (klass);
}
/* interface_offsets might not be set for dynamic classes */
- if (oklass->ref_info_handle && !oklass->interface_bitmap)
+ if (oklass->ref_info_handle && !oklass->interface_bitmap) {
/*
* oklass might be a generic type parameter but they have
* interface_offsets set.
*/
- return mono_reflection_call_is_assignable_to (oklass, klass);
+ gboolean result = mono_reflection_call_is_assignable_to (oklass, klass, &error);
+ if (!is_ok (&error)) {
+ mono_error_cleanup (&error);
+ return FALSE;
+ }
+ return result;
+ }
if (!oklass->interface_bitmap)
/* Happens with generic instances of not-yet created dynamic types */
return FALSE;
return TRUE;
if (mono_class_has_variant_generic_params (klass)) {
- MonoError error;
int i;
mono_class_setup_interfaces (oklass, &error);
if (!mono_error_ok (&error)) {
if (image_is_dynamic (image)) {
MonoClass *tmp_handle_class;
- gpointer obj = mono_lookup_dynamic_token_class (image, token, TRUE, &tmp_handle_class, context);
+ gpointer obj = mono_lookup_dynamic_token_class (image, token, TRUE, &tmp_handle_class, context, error);
+ mono_error_assert_ok (error);
g_assert (tmp_handle_class);
if (handle_class)
*handle_class = tmp_handle_class;
return NULL;
}
-/**
- * This function might need to call runtime functions so it can't be part
- * of the metadata library.
- */
-static MonoLookupDynamicToken lookup_dynamic = NULL;
-
-void
-mono_install_lookup_dynamic_token (MonoLookupDynamicToken func)
-{
- lookup_dynamic = func;
-}
-
gpointer
-mono_lookup_dynamic_token (MonoImage *image, guint32 token, MonoGenericContext *context)
+mono_lookup_dynamic_token (MonoImage *image, guint32 token, MonoGenericContext *context, MonoError *error)
{
MonoClass *handle_class;
-
- return lookup_dynamic (image, token, TRUE, &handle_class, context);
+ mono_error_init (error);
+ return mono_reflection_lookup_dynamic_token (image, token, TRUE, &handle_class, context, error);
}
gpointer
-mono_lookup_dynamic_token_class (MonoImage *image, guint32 token, gboolean valid_token, MonoClass **handle_class, MonoGenericContext *context)
+mono_lookup_dynamic_token_class (MonoImage *image, guint32 token, gboolean valid_token, MonoClass **handle_class, MonoGenericContext *context, MonoError *error)
{
- return lookup_dynamic (image, token, valid_token, handle_class, context);
+ return mono_reflection_lookup_dynamic_token (image, token, valid_token, handle_class, context, error);
}
static MonoGetCachedClassInfo get_cached_class_info = NULL;
static gpointer
cominterop_get_ccw (MonoObject* object, MonoClass* itf);
+static gpointer
+cominterop_get_ccw_checked (MonoObject *object, MonoClass *itf, MonoError *error);
+
+
static MonoObject*
cominterop_get_ccw_object (MonoCCWInterface* ccw_entry, gboolean verify);
}
-static void cominterop_raise_hr_exception (int hr)
+static void cominterop_set_hr_error (MonoError *oerror, int hr)
{
static MonoMethod* throw_exception_for_hr = NULL;
MonoError error;
throw_exception_for_hr = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetExceptionForHR", 1);
ex = (MonoException*)mono_runtime_invoke_checked (throw_exception_for_hr, NULL, params, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_error_assert_ok (&error);
- mono_raise_exception (ex);
+ mono_error_set_exception_instance (oerror, ex);
}
/**
- * cominterop_get_interface:
+ * cominterop_get_interface_checked:
* @obj: managed wrapper object containing COM object
* @ic: interface type to retrieve for COM object
+ * @error: set on error
*
- * Returns: the COM interface requested
+ * Returns: the COM interface requested. On failure returns NULL and sets @error
*/
static gpointer
-cominterop_get_interface (MonoComObject* obj, MonoClass* ic, gboolean throw_exception)
+cominterop_get_interface_checked (MonoComObject* obj, MonoClass* ic, MonoError *error)
{
gpointer itf = NULL;
g_assert (ic);
g_assert (MONO_CLASS_IS_INTERFACE (ic));
+ mono_error_init (error);
+
mono_cominterop_lock ();
if (obj->itf_hash)
itf = g_hash_table_lookup (obj->itf_hash, GUINT_TO_POINTER ((guint)ic->interface_id));
int hr;
g_assert(found);
hr = ves_icall_System_Runtime_InteropServices_Marshal_QueryInterfaceInternal (obj->iunknown, iid, &itf);
- if (hr < 0 && throw_exception) {
- cominterop_raise_hr_exception (hr);
+ if (hr < 0) {
+ cominterop_set_hr_error (error, hr);
}
if (hr >= 0 && itf) {
}
}
+ return itf;
+}
+
+/**
+ * cominterop_get_interface:
+ * @obj: managed wrapper object containing COM object
+ * @ic: interface type to retrieve for COM object
+ *
+ * Returns: the COM interface requested
+ */
+static gpointer
+cominterop_get_interface (MonoComObject *obj, MonoClass *ic, gboolean throw_exception)
+{
+ MonoError error;
+ gpointer itf = cominterop_get_interface_checked (obj, ic, &error);
+ if (!is_ok (&error)) {
+ if (throw_exception) {
+ mono_error_set_pending_exception (&error);
+ return NULL;
+ } else {
+ mono_error_cleanup (&error);
+ }
+ }
+
if (throw_exception)
g_assert (itf);
mono_class_init (klass);
ret = mono_type_get_object_checked (domain, handle, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_error_set_pending_exception (&error);
return ret;
}
}
static void*
-cominterop_get_idispatch_for_object (MonoObject* object)
+cominterop_get_idispatch_for_object (MonoObject* object, MonoError *error)
{
+ mono_error_init (error);
if (!object)
return NULL;
if (cominterop_object_is_rcw (object)) {
- return cominterop_get_interface (((MonoComInteropProxy*)((MonoTransparentProxy*)object)->rp)->com_object,
- mono_class_get_idispatch_class (), TRUE);
+ return cominterop_get_interface_checked (((MonoComInteropProxy*)((MonoTransparentProxy*)object)->rp)->com_object,
+ mono_class_get_idispatch_class (), error);
}
else {
MonoClass* klass = mono_object_class (object);
- if (!cominterop_can_support_dispatch (klass) )
- cominterop_raise_hr_exception (MONO_E_NOINTERFACE);
- return cominterop_get_ccw (object, mono_class_get_idispatch_class ());
+ if (!cominterop_can_support_dispatch (klass) ) {
+ cominterop_set_hr_error (error, MONO_E_NOINTERFACE);
+ return NULL;
+ }
+ return cominterop_get_ccw_checked (object, mono_class_get_idispatch_class (), error);
}
}
ves_icall_System_Runtime_InteropServices_Marshal_GetIUnknownForObjectInternal (MonoObject* object)
{
#ifndef DISABLE_COM
+ MonoError error;
+
if (!object)
return NULL;
return ((MonoComInteropProxy*)real_proxy)->com_object->iunknown;
}
else {
- return cominterop_get_ccw (object, mono_class_get_iunknown_class ());
+ void* ccw_entry = cominterop_get_ccw_checked (object, mono_class_get_iunknown_class (), &error);
+ mono_error_set_pending_exception (&error);
+ return ccw_entry;
}
#else
g_assert_not_reached ();
ves_icall_System_Runtime_InteropServices_Marshal_GetIDispatchForObjectInternal (MonoObject* object)
{
#ifndef DISABLE_COM
- return cominterop_get_idispatch_for_object (object);
+ MonoError error;
+ void* idisp = cominterop_get_idispatch_for_object (object, &error);
+ mono_error_set_pending_exception (&error);
+ return idisp;
#else
g_assert_not_reached ();
#endif
ves_icall_System_Runtime_InteropServices_Marshal_GetCCW (MonoObject* object, MonoReflectionType* type)
{
#ifndef DISABLE_COM
+ MonoError error;
MonoClass* klass = NULL;
void* itf = NULL;
g_assert (type);
return NULL;
}
- itf = cominterop_get_ccw (object, klass);
- g_assert (itf);
+ itf = cominterop_get_ccw_checked (object, klass, &error);
+ mono_error_set_pending_exception (&error);
return itf;
#else
g_assert_not_reached ();
ves_icall_System_ComObject_GetInterfaceInternal (MonoComObject* obj, MonoReflectionType* type, MonoBoolean throw_exception)
{
#ifndef DISABLE_COM
+ MonoError error;
MonoClass *klass = mono_type_get_class (type->type);
if (!mono_class_init (klass)) {
mono_set_pending_exception (mono_class_get_exception_for_failure (klass));
return NULL;
}
- return cominterop_get_interface (obj, klass, (gboolean)throw_exception);
+ gpointer itf = cominterop_get_interface_checked (obj, klass, &error);
+ if (throw_exception)
+ mono_error_set_pending_exception (&error);
+ else
+ mono_error_cleanup (&error);
+ return itf;
#else
g_assert_not_reached ();
#endif
}
/**
- * cominterop_get_ccw:
+ * cominterop_get_ccw_checked:
* @object: a pointer to the object
* @itf: interface type needed
+ * @error: set on error
*
* Returns: a value indicating if the object is a
- * Runtime Callable Wrapper (RCW) for a COM object
+ * Runtime Callable Wrapper (RCW) for a COM object.
+ * On failure returns NULL and sets @error.
*/
static gpointer
-cominterop_get_ccw (MonoObject* object, MonoClass* itf)
+cominterop_get_ccw_checked (MonoObject* object, MonoClass* itf, MonoError *error)
{
- MonoError error;
int i;
MonoCCW *ccw = NULL;
MonoCCWInterface* ccw_entry = NULL;
GList *ccw_list, *ccw_list_item;
MonoCustomAttrInfo *cinfo = NULL;
+ mono_error_init (error);
+
if (!object)
return NULL;
g_hash_table_insert (ccw_hash, GINT_TO_POINTER (mono_object_hash (object)), ccw_list);
mono_cominterop_unlock ();
/* register for finalization to clean up ccw */
- mono_object_register_finalizer (object, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_object_register_finalizer (object, error);
+ return_val_if_nok (error, NULL);
}
- cinfo = mono_custom_attrs_from_class_checked (itf, &error);
- mono_error_assert_ok (&error);
+ cinfo = mono_custom_attrs_from_class_checked (itf, error);
+ mono_error_assert_ok (error);
if (cinfo) {
static MonoClass* coclass_attribute = NULL;
if (!coclass_attribute)
return ccw_entry;
}
+/**
+ * cominterop_get_ccw:
+ * @object: a pointer to the object
+ * @itf: interface type needed
+ *
+ * Returns: a value indicating if the object is a
+ * Runtime Callable Wrapper (RCW) for a COM object
+ */
+static gpointer
+cominterop_get_ccw (MonoObject* object, MonoClass* itf)
+{
+ MonoError error;
+ gpointer ccw_entry = cominterop_get_ccw_checked (object, itf, &error);
+ mono_error_set_pending_exception (&error);
+ return ccw_entry;
+}
+
static gboolean
mono_marshal_free_ccw_entry (gpointer key, gpointer value, gpointer user_data)
{
cominterop_ccw_getfreethreadedmarshaler (MonoCCW* ccw, MonoObject* object, gpointer* ppv)
{
#ifdef HOST_WIN32
+ MonoError error;
if (!ccw->free_marshaler) {
int ret = 0;
gpointer tunk;
- tunk = cominterop_get_ccw (object, mono_class_get_iunknown_class ());
+ tunk = cominterop_get_ccw_checked (object, mono_class_get_iunknown_class (), &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
ret = CoCreateFreeThreadedMarshaler (tunk, (LPUNKNOWN*)&ccw->free_marshaler);
}
/* handle IUnknown special */
if (cominterop_class_guid_equal (riid, mono_class_get_iunknown_class ())) {
- *ppv = cominterop_get_ccw (object, mono_class_get_iunknown_class ());
+ *ppv = cominterop_get_ccw_checked (object, mono_class_get_iunknown_class (), &error);
+ mono_error_assert_ok (&error);
/* remember to addref on QI */
cominterop_ccw_addref ((MonoCCWInterface *)*ppv);
return MONO_S_OK;
if (!cominterop_can_support_dispatch (klass))
return MONO_E_NOINTERFACE;
- *ppv = cominterop_get_ccw (object, mono_class_get_idispatch_class ());
+ *ppv = cominterop_get_ccw_checked (object, mono_class_get_idispatch_class (), &error);
+ mono_error_assert_ok (&error);
/* remember to addref on QI */
cominterop_ccw_addref ((MonoCCWInterface *)*ppv);
return MONO_S_OK;
klass_iter = klass_iter->parent;
}
if (itf) {
- *ppv = cominterop_get_ccw (object, itf);
+ *ppv = cominterop_get_ccw_checked (object, itf, &error);
+ if (!is_ok (&error)) {
+ mono_error_cleanup (&error); /* FIXME don't swallow the error */
+ return MONO_E_NOINTERFACE;
+ }
/* remember to addref on QI */
cominterop_ccw_addref ((MonoCCWInterface *)*ppv);
return MONO_S_OK;
hr = mono_marshal_safe_array_get_lbound (safearray, i+1, &lbound);
if (hr < 0) {
- cominterop_raise_hr_exception (hr);
+ cominterop_set_hr_error (&error, hr);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
}
if (lbound != 0)
bounded = TRUE;
hr = mono_marshal_safe_array_get_ubound (safearray, i+1, &ubound);
if (hr < 0) {
- cominterop_raise_hr_exception (hr);
+ cominterop_set_hr_error (&error, hr);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
}
cursize = ubound-lbound+1;
sizes [i] = cursize;
static
gpointer mono_marshal_safearray_get_value (gpointer safearray, gpointer indices)
{
+ MonoError error;
gpointer result;
#ifdef HOST_WIN32
int hr = SafeArrayPtrOfIndex (safearray, indices, &result);
if (hr < 0) {
- cominterop_raise_hr_exception (hr);
+ cominterop_set_hr_error (&error, hr);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
}
#else
if (com_provider == MONO_COM_MS && init_com_provider_ms ()) {
int hr = safe_array_ptr_of_index_ms (safearray, (glong *)indices, &result);
if (hr < 0) {
- cominterop_raise_hr_exception (hr);
+ cominterop_set_hr_error (&error, hr);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
}
} else {
g_assert_not_reached ();
static
gboolean mono_marshal_safearray_next (gpointer safearray, gpointer indices)
{
+ MonoError error;
int i;
int dim = mono_marshal_safearray_get_dim (safearray);
gboolean ret= TRUE;
hr = mono_marshal_safe_array_get_ubound (safearray, i+1, &ubound);
if (hr < 0) {
- cominterop_raise_hr_exception (hr);
+ cominterop_set_hr_error (&error, hr);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
}
if (++pIndices[i] <= ubound) {
hr = mono_marshal_safe_array_get_lbound (safearray, i+1, &lbound);
if (hr < 0) {
- cominterop_raise_hr_exception (hr);
+ cominterop_set_hr_error (&error, hr);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
}
pIndices[i] = lbound;
static
void mono_marshal_safearray_set_value (gpointer safearray, gpointer indices, gpointer value)
{
+ MonoError error;
#ifdef HOST_WIN32
int hr = SafeArrayPutElement (safearray, indices, value);
- if (hr < 0)
- cominterop_raise_hr_exception (hr);
+ if (hr < 0) {
+ cominterop_set_hr_error (&error, hr);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ }
#else
if (com_provider == MONO_COM_MS && init_com_provider_ms ()) {
int hr = safe_array_put_element_ms (safearray, (glong *)indices, (void **)value);
if (hr < 0) {
- cominterop_raise_hr_exception (hr);
+ cominterop_set_hr_error (&error, hr);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
}
} else
g_assert_not_reached ();
method = mono_class_get_method_from_name (klass, "BeginInvoke", -1);
g_assert (method != NULL);
- mono_threadpool_ms_begin_invoke (domain, (MonoObject*) load_value, method, NULL);
+ mono_threadpool_ms_begin_invoke (domain, (MonoObject*) load_value, method, NULL, &error);
+ if (!is_ok (&error)) {
+ g_warning ("Couldn't invoke System.Console cancel handler due to %s", mono_error_get_message (&error));
+ mono_error_cleanup (&error);
+ }
}
static int need_cancel = FALSE;
mono_loader_cleanup ();
mono_classes_cleanup ();
mono_assemblies_cleanup ();
- mono_images_cleanup ();
mono_debug_cleanup ();
+ mono_images_cleanup ();
mono_metadata_cleanup ();
mono_native_tls_free (appdomain_thread_id);
mono_raise_exception (ex);
}
-void
+/**
+ * mono_error_set_pending_exception:
+ * @error: The error
+ *
+ *
+ * If @error is set, convert it to an exception and set the pending exception for the current icall.
+ * Returns TRUE if @error was set, or FALSE otherwise, so that you can write:
+ * if (mono_error_set_pending_exception (error)) {
+ * { ... cleanup code ... }
+ * return;
+ * }
+ */
+gboolean
mono_error_set_pending_exception (MonoError *error)
{
MonoException *ex = mono_error_convert_to_exception (error);
- if (ex)
+ if (ex) {
mono_set_pending_exception (ex);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
}
return mono_handle_arena_elevate (mono_handle_arena_current (), handle);
}
-#ifndef CHECKED_BUILD
+#ifndef ENABLE_CHECKED_BUILD
#define mono_handle_obj(handle) ((handle)->__private_obj)
ICALL(ASSEMB_2, "basic_init", mono_image_basic_init)
ICALL_TYPE(CATTRB, "System.Reflection.Emit.CustomAttributeBuilder", CATTRB_1)
-ICALL(CATTRB_1, "GetBlob", mono_reflection_get_custom_attrs_blob)
+ICALL(CATTRB_1, "GetBlob", ves_icall_System_Reflection_Emit_CustomAttributeBuilder_GetBlob)
#ifndef DISABLE_REFLECTION_EMIT
ICALL_TYPE(DERIVEDTYPE, "System.Reflection.Emit.DerivedType", DERIVEDTYPE_1)
ICALL_EXPORT MonoObject *
ves_icall_System_Array_GetValueImpl (MonoArray *arr, guint32 pos)
{
+ MonoError error;
MonoClass *ac;
gint32 esize;
gpointer *ea;
+ MonoObject *result = NULL;
ac = (MonoClass *)arr->obj.vtable->klass;
esize = mono_array_element_size (ac);
ea = (gpointer*)((char*)arr->vector + (pos * esize));
- if (ac->element_class->valuetype)
- return mono_value_box (arr->obj.vtable->domain, ac->element_class, ea);
- else
- return (MonoObject *)*ea;
+ if (ac->element_class->valuetype) {
+ result = mono_value_box_checked (arr->obj.vtable->domain, ac->element_class, ea, &error);
+ mono_error_set_pending_exception (&error);
+ } else
+ result = (MonoObject *)*ea;
+ return result;
}
ICALL_EXPORT MonoObject *
ICALL_EXPORT void
ves_icall_System_Array_SetValueImpl (MonoArray *arr, MonoObject *value, guint32 pos)
{
+ MonoError error;
MonoClass *ac, *vc, *ec;
gint32 esize, vsize;
gpointer *ea, *va;
gint64 i64 = 0;
gdouble r64 = 0;
+ mono_error_init (&error);
+
if (value)
vc = value->vtable->klass;
else
}
if (!ec->valuetype) {
- if (!mono_object_isinst (value, ec))
+ gboolean castOk = (NULL != mono_object_isinst_checked (value, ec, &error));
+ if (mono_error_set_pending_exception (&error))
+ return;
+ if (!castOk)
INVALID_CAST;
mono_gc_wbarrier_set_arrayref (arr, ea, (MonoObject*)value);
return;
}
- if (mono_object_isinst (value, ec)) {
+ if (mono_object_isinst_checked (value, ec, &error)) {
if (ec->has_references)
mono_value_copy (ea, (char*)value + sizeof (MonoObject), ec);
else
mono_gc_memmove_atomic (ea, (char *)value + sizeof (MonoObject), esize);
return;
}
+ if (mono_error_set_pending_exception (&error))
+ return;
if (!vc->valuetype)
INVALID_CAST;
return result;
}
+ICALL_EXPORT MonoArray*
+ves_icall_System_Reflection_Emit_CustomAttributeBuilder_GetBlob (MonoReflectionAssembly *assembly, MonoObject *ctor, MonoArray *ctorArgs, MonoArray *properties, MonoArray *propValues, MonoArray *fields, MonoArray* fieldValues)
+{
+ MonoError error;
+ MonoArray *result = mono_reflection_get_custom_attrs_blob_checked (assembly, ctor, ctorArgs, properties, propValues, fields, fieldValues, &error);
+ mono_error_set_pending_exception (&error);
+ return result;
+}
+
static gboolean
get_caller (MonoMethod *m, gint32 no, gint32 ilo, gboolean managed, gpointer data)
{
MonoError error;
MonoClass *klass = mono_class_from_mono_type (type->type);
mono_class_init_checked (klass, &error);
- mono_error_raise_exception (&error);
- return mono_object_isinst (obj, klass) != NULL;
+ if (!is_ok (&error)) {
+ mono_error_set_pending_exception (&error);
+ return FALSE;
+ }
+ guint32 result = (mono_object_isinst_checked (obj, klass, &error) != NULL);
+ mono_error_set_pending_exception (&error);
+ return result;
}
ICALL_EXPORT guint32
types [i] = t->type;
}
- geninst = mono_reflection_bind_generic_parameters (type, count, types);
+ geninst = mono_reflection_bind_generic_parameters (type, count, types, &error);
g_free (types);
- if (!geninst)
+ if (!geninst) {
+ mono_error_set_pending_exception (&error);
return NULL;
+ }
klass = mono_class_from_mono_type (geninst);
}
if (this_arg) {
- if (!mono_object_isinst (this_arg, m->klass)) {
+ if (!mono_object_isinst_checked (this_arg, m->klass, &error)) {
+ if (!is_ok (&error)) {
+ mono_gc_wbarrier_generic_store (exc, (MonoObject*) mono_error_convert_to_exception (&error));
+ return NULL;
+ }
char *this_name = mono_type_get_full_name (mono_object_get_class (this_arg));
char *target_name = mono_type_get_full_name (m->klass);
char *msg = g_strdup_printf ("Object of type '%s' doesn't match target type '%s'", this_name, target_name);
ICALL_EXPORT MonoObject *
ves_icall_InternalExecute (MonoReflectionMethod *method, MonoObject *this_arg, MonoArray *params, MonoArray **outArgs)
{
+ MonoError error;
MonoDomain *domain = mono_object_domain (method);
MonoMethod *m = method->method;
MonoMethodSignature *sig = mono_method_signature (m);
MonoClassField* field = mono_class_get_field_from_name (k, str);
if (field) {
MonoClass *field_klass = mono_class_from_mono_type (field->type);
- if (field_klass->valuetype)
- result = mono_value_box (domain, field_klass, (char *)this_arg + field->offset);
- else
+ if (field_klass->valuetype) {
+ result = mono_value_box_checked (domain, field_klass, (char *)this_arg + field->offset, &error);
+ mono_error_set_pending_exception (&error);
+ /* fallthru to cleanup */
+ } else
result = (MonoObject *)*((gpointer *)((char *)this_arg + field->offset));
out_args = mono_array_new (domain, mono_defaults.object_class, 1);
mono_ptr_array_destroy (tmp_array);
- if (!str)
- g_free (str);
+ g_free (str);
return res;
}
if (!exportedOnly || mono_module_type_is_visible (tdef, image, i + 1)) {
klass = mono_class_get_checked (image, (i + 1) | MONO_TOKEN_TYPE_DEF, error);
mono_loader_assert_no_error (); /* Plug any leaks */
- mono_error_assert_ok (error);
if (klass) {
rt = mono_type_get_object_checked (domain, &klass->byval_arg, error);
mono_metadata_decode_blob_size (sig, &sig);
return (*sig != 0x6);
} else {
+ MonoError error;
MonoClass *handle_class;
- if (!mono_lookup_dynamic_token_class (image, token, FALSE, &handle_class, NULL))
+ if (!mono_lookup_dynamic_token_class (image, token, FALSE, &handle_class, NULL, &error)) {
+ mono_error_cleanup (&error); /* just probing, ignore error */
return FALSE;
+ }
return mono_defaults.methodhandle_class == handle_class;
}
if (image_is_dynamic (image)) {
if ((table == MONO_TABLE_TYPEDEF) || (table == MONO_TABLE_TYPEREF)) {
- klass = (MonoClass *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, NULL);
+ klass = (MonoClass *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, NULL, &error);
+ mono_error_cleanup (&error);
return klass ? &klass->byval_arg : NULL;
}
init_generic_context_from_args (&context, type_args, method_args);
- klass = (MonoClass *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, &context);
+ klass = (MonoClass *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, &context, &error);
+ mono_error_cleanup (&error);
return klass ? &klass->byval_arg : NULL;
}
}
if (image_is_dynamic (image)) {
- if (table == MONO_TABLE_METHOD)
- return (MonoMethod *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, NULL);
+ if (table == MONO_TABLE_METHOD) {
+ method = (MonoMethod *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, NULL, &error);
+ mono_error_cleanup (&error);
+ return method;
+ }
if ((table == MONO_TABLE_MEMBERREF) && !(mono_memberref_is_method (image, token))) {
*resolve_error = ResolveTokenError_BadTable;
}
init_generic_context_from_args (&context, type_args, method_args);
- return (MonoMethod *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, &context);
+ method = (MonoMethod *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, &context, &error);
+ mono_error_cleanup (&error);
+ return method;
}
if ((index <= 0) || (index > image->tables [table].rows)) {
}
ICALL_EXPORT MonoString*
-ves_icall_System_Reflection_Module_ResolveStringToken (MonoImage *image, guint32 token, MonoResolveTokenError *error)
+ves_icall_System_Reflection_Module_ResolveStringToken (MonoImage *image, guint32 token, MonoResolveTokenError *resolve_error)
{
+ MonoError error;
int index = mono_metadata_token_index (token);
- *error = ResolveTokenError_Other;
+ *resolve_error = ResolveTokenError_Other;
/* Validate token */
if (mono_metadata_token_code (token) != MONO_TOKEN_STRING) {
- *error = ResolveTokenError_BadTable;
+ *resolve_error = ResolveTokenError_BadTable;
return NULL;
}
- if (image_is_dynamic (image))
- return (MonoString *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, NULL);
+ if (image_is_dynamic (image)) {
+ MonoString * result = (MonoString *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, NULL, &error);
+ mono_error_cleanup (&error);
+ return result;
+ }
if ((index <= 0) || (index >= image->heap_us.size)) {
- *error = ResolveTokenError_OutOfRange;
+ *resolve_error = ResolveTokenError_OutOfRange;
return NULL;
}
}
if (image_is_dynamic (image)) {
- if (table == MONO_TABLE_FIELD)
- return (MonoClassField *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, NULL);
+ if (table == MONO_TABLE_FIELD) {
+ field = (MonoClassField *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, NULL, &error);
+ mono_error_cleanup (&error);
+ return field;
+ }
if (mono_memberref_is_method (image, token)) {
*resolve_error = ResolveTokenError_BadTable;
}
init_generic_context_from_args (&context, type_args, method_args);
- return (MonoClassField *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, &context);
+ field = (MonoClassField *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, &context, &error);
+ mono_error_cleanup (&error);
+ return field;
}
if ((index <= 0) || (index > image->tables [table].rows)) {
mono_class_init_checked (delegate_class, &error);
mono_error_raise_exception (&error);
- mono_assert (delegate_class->parent == mono_defaults.multicastdelegate_class);
+ if (!(delegate_class->parent == mono_defaults.multicastdelegate_class)) {
+ /* FIXME improve this exception message */
+ mono_error_set_execution_engine (&error, "file %s: line %d (%s): assertion failed: (%s)", __FILE__, __LINE__,
+ __func__,
+ "delegate_class->parent == mono_defaults.multicastdelegate_class");
+ mono_error_set_pending_exception (&error);
+ return NULL;
+ }
if (mono_security_core_clr_enabled ()) {
if (!mono_security_core_clr_ensure_delegate_creation (method, throwOnBindFailure))
return NULL;
}
- tp->custom_type_info = (mono_object_isinst (this_obj, mono_defaults.iremotingtypeinfo_class) != NULL);
+ tp->custom_type_info = (mono_object_isinst_checked (this_obj, mono_defaults.iremotingtypeinfo_class, &error) != NULL);
+ if (!is_ok (&error)) {
+ mono_error_set_pending_exception (&error);
+ return NULL;
+ }
tp->remote_class = mono_remote_class (domain, class_name, klass, &error);
if (!is_ok (&error)) {
mono_error_set_pending_exception (&error);
ICALL_EXPORT MonoObject*
mono_TypedReference_ToObject (MonoTypedRef* tref)
{
+ MonoError error;
+ MonoObject *result = NULL;
if (MONO_TYPE_IS_REFERENCE (tref->type)) {
MonoObject** objp = (MonoObject **)tref->value;
return *objp;
}
- return mono_value_box (mono_domain_get (), tref->klass, tref->value);
+ result = mono_value_box_checked (mono_domain_get (), tref->klass, tref->value, &error);
+ mono_error_set_pending_exception (&error);
+ return result;
}
ICALL_EXPORT MonoTypedRef
i = ((MonoImageLoader*)image->loader)->load_tables (image);
g_assert (image->heap_guid.data);
- g_assert (image->heap_guid.size >= 16);
- image->guid = mono_guid_to_string ((guint8*)image->heap_guid.data);
+ if (!image->metadata_only) {
+ g_assert (image->heap_guid.size >= 16);
+
+ image->guid = mono_guid_to_string ((guint8*)image->heap_guid.data);
+ } else {
+ /* PPDB files have no guid */
+ guint8 empty_guid [16];
+
+ memset (empty_guid, 0, sizeof (empty_guid));
+
+ image->guid = mono_guid_to_string (empty_guid);
+ }
return i;
}
goto done;
}
- if (image->loader == &pe_loader && !mono_verifier_verify_cli_data (image, &errors))
+ if (image->loader == &pe_loader && !image->metadata_only && !mono_verifier_verify_cli_data (image, &errors))
goto invalid_image;
if (!mono_image_load_cli_data (image))
goto invalid_image;
- if (image->loader == &pe_loader && !mono_verifier_verify_table_data (image, &errors))
+ if (image->loader == &pe_loader && !image->metadata_only && !mono_verifier_verify_table_data (image, &errors))
goto invalid_image;
mono_image_load_names (image);
MonoClass *handle_class;
*retklass = NULL;
- result = (MonoClassField *)mono_lookup_dynamic_token_class (image, token, TRUE, &handle_class, context);
+ MonoError inner_error;
+ result = (MonoClassField *)mono_lookup_dynamic_token_class (image, token, TRUE, &handle_class, context, &inner_error);
+ mono_error_cleanup (&inner_error);
// This checks the memberref type as well
if (!result || handle_class != mono_defaults.fieldhandle_class) {
mono_error_set_bad_image (error, image, "Bad field token 0x%08x", token);
if (image_is_dynamic (image)) {
MonoClass *handle_class;
- result = (MonoMethod *)mono_lookup_dynamic_token_class (image, token, TRUE, &handle_class, context);
+ result = (MonoMethod *)mono_lookup_dynamic_token_class (image, token, TRUE, &handle_class, context, error);
+ mono_error_assert_ok (error);
mono_loader_assert_no_error ();
// This checks the memberref type as well
}
static void
-register_icall (gpointer func, const char *name, const char *sigstr, gboolean save)
+register_icall (gpointer func, const char *name, const char *sigstr, gboolean no_wrapper)
{
MonoMethodSignature *sig = mono_create_icall_signature (sigstr);
- mono_register_jit_icall (func, name, sig, save);
+ mono_register_jit_icall (func, name, sig, no_wrapper);
}
MonoMethodSignature*
mono_native_tls_alloc (&load_type_info_tls_id, NULL);
}
+static MonoObject*
+mono_object_isinst_icall (MonoObject *obj, MonoClass *klass)
+{
+ MonoError error;
+ MonoObject *result = mono_object_isinst_checked (obj, klass, &error);
+ mono_error_set_pending_exception (&error);
+ return result;
+}
+
void
mono_marshal_init (void)
{
register_icall (mono_string_to_byvalstr, "mono_string_to_byvalstr", "void ptr ptr int32", FALSE);
register_icall (mono_string_to_byvalwstr, "mono_string_to_byvalwstr", "void ptr ptr int32", FALSE);
register_icall (g_free, "g_free", "void ptr", FALSE);
- register_icall (mono_object_isinst, "mono_object_isinst", "object object ptr", FALSE);
+ register_icall (mono_object_isinst_icall, "mono_object_isinst_icall", "object object ptr", FALSE);
register_icall (mono_struct_delete_old, "mono_struct_delete_old", "void ptr ptr", FALSE);
register_icall (mono_delegate_begin_invoke, "mono_delegate_begin_invoke", "object object ptr", FALSE);
register_icall (mono_delegate_end_invoke, "mono_delegate_end_invoke", "object object ptr", FALSE);
if (delegate->method_is_virtual)
method = mono_object_get_virtual_method (delegate->target, method);
- if (mono_method_signature (method)->pinvoke) {
+ if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) {
const char *exc_class, *exc_arg;
gpointer ftnptr;
if (use_aot_wrappers) {
wrapper = mono_marshal_get_native_func_wrapper_aot (klass);
- this_obj = mono_value_box (mono_domain_get (), mono_defaults.int_class, &ftn);
+ this_obj = mono_value_box_checked (mono_domain_get (), mono_defaults.int_class, &ftn, &error);
+ if (!is_ok (&error)) {
+ mono_error_set_pending_exception (&error);
+ return NULL;
+ }
} else {
memset (&piinfo, 0, sizeof (piinfo));
parse_unmanaged_function_pointer_attr (klass, &piinfo);
}
default: {
- char *msg = g_strdup_printf ("marshalling conversion %d not implemented", conv);
- MonoException *exc = mono_get_exception_not_implemented (msg);
- g_warning ("%s", msg);
- g_free (msg);
- mono_raise_exception (exc);
+ g_error ("marshalling conversion %d not implemented", conv);
}
}
}
method = mono_get_delegate_invoke (klass);
g_assert (method);
- return mono_threadpool_ms_begin_invoke (mono_domain_get (), (MonoObject*) delegate, method, params);
+ MonoAsyncResult *result = mono_threadpool_ms_begin_invoke (mono_domain_get (), (MonoObject*) delegate, method, params, &error);
+ mono_error_set_pending_exception (&error);
+ return result;
}
#ifndef DISABLE_JIT
EmitMarshalContext m;
g_assert (method != NULL);
- g_assert (!mono_method_signature (method)->pinvoke);
+ g_assert (!(method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL));
/*
* FIXME: Should cache the method+delegate type pair, since the same method
static MonoObject *
mono_marshal_isinst_with_cache (MonoObject *obj, MonoClass *klass, uintptr_t *cache)
{
- MonoObject *isinst = mono_object_isinst (obj, klass);
+ MonoError error;
+ MonoObject *isinst = mono_object_isinst_checked (obj, klass, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
#ifndef DISABLE_REMOTING
if (obj->vtable->klass == mono_defaults.transparent_proxy_class)
/*if (mono_object_isinst (value, aklass)) */
mono_mb_emit_ldarg (mb, 2);
mono_mb_emit_ldloc (mb, aklass);
- mono_mb_emit_icall (mb, mono_object_isinst);
+ mono_mb_emit_icall (mb, mono_object_isinst_icall);
b2 = mono_mb_emit_branch (mb, CEE_BRFALSE);
/* do_store: */
/*if (mono_object_isinst (value, aklass)) */
mono_mb_emit_ldarg (mb, 2);
mono_mb_emit_ldloc (mb, aklass);
- mono_mb_emit_icall (mb, mono_object_isinst);
+ mono_mb_emit_icall (mb, mono_object_isinst_icall);
b2 = mono_mb_emit_branch (mb, CEE_BRFALSE);
/* if (vklass->idepth < aklass->idepth) goto failue */
mono_mb_emit_ldarg (mb, 2);
mono_mb_emit_ldloc (mb, aklass);
- mono_mb_emit_icall (mb, mono_object_isinst);
+ mono_mb_emit_icall (mb, mono_object_isinst_icall);
b4 = mono_mb_emit_branch (mb, CEE_BRTRUE);
mono_mb_patch_addr (mb, b4, copy_pos - (b4 + 4));
guint32 sig;
const char *ptr;
- if (image_is_dynamic (image))
- return (MonoMethodSignature *)mono_lookup_dynamic_token (image, token, NULL);
+ if (image_is_dynamic (image)) {
+ ret = (MonoMethodSignature *)mono_lookup_dynamic_token (image, token, NULL, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ return ret;
+ }
g_assert (mono_metadata_token_table(token) == MONO_TABLE_STANDALONESIG);
#include "mono/utils/mono-tls.h"
#include "mono/utils/mono-coop-mutex.h"
-#if 1
-#ifdef __GNUC__
-#define mono_assert(expr) G_STMT_START{ \
- if (!(expr)) \
- { \
- MonoException *ex; \
- char *msg = g_strdup_printf ("file %s: line %d (%s): " \
- "assertion failed: (%s)", __FILE__, __LINE__, \
- __PRETTY_FUNCTION__, #expr); \
- ex = mono_get_exception_execution_engine (msg); \
- g_free (msg); \
- mono_raise_exception (ex); \
- }; }G_STMT_END
-
-#define mono_assert_not_reached() G_STMT_START{ \
- MonoException *ex; \
- char *msg = g_strdup_printf ("file %s: line %d (%s): " \
- "should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__); \
- ex = mono_get_exception_execution_engine (msg); \
- g_free (msg); \
- mono_raise_exception (ex); \
-}G_STMT_END
-#else /* not GNUC */
-#define mono_assert(expr) G_STMT_START{ \
- if (!(expr)) \
- { \
- MonoException *ex; \
- char *msg = g_strdup_printf ("file %s: line %d: " \
- "assertion failed: (%s)", __FILE__, __LINE__, \
- #expr); \
- ex = mono_get_exception_execution_engine (msg); \
- g_free (msg); \
- mono_raise_exception (ex); \
- }; }G_STMT_END
-
-#define mono_assert_not_reached() G_STMT_START{ \
- MonoException *ex; \
- char *msg = g_strdup_printf ("file %s: line %d): " \
- "should not be reached", __FILE__, __LINE__); \
- ex = mono_get_exception_execution_engine (msg); \
- g_free (msg); \
- mono_raise_exception (ex); \
-}G_STMT_END
-#endif
-#else
-#define mono_assert(expr) g_assert(expr)
-#define mono_assert_not_reached() g_assert_not_reached()
-#endif
-
/* Use this as MONO_CHECK_ARG_NULL (arg,expr,) in functions returning void */
#define MONO_CHECK_ARG(arg, expr, retval) G_STMT_START{ \
if (G_UNLIKELY (!(expr))) \
#define IS_MONOTYPE(obj) (!(obj) || (((MonoObject*)(obj))->vtable->klass->image == mono_defaults.corlib && ((MonoReflectionType*)(obj))->type != NULL))
-/*
- * Make sure the argument, which should be a System.Type is a System.MonoType object
- * or equivalent, and not an instance of
- * a user defined subclass of System.Type. This should be used in places were throwing
- * an exception is safe.
- */
-#define CHECK_MONOTYPE(obj) do { \
- if (!IS_MONOTYPE (obj)) \
- mono_raise_exception (mono_get_exception_not_supported ("User defined subclasses of System.Type are not yet supported")); \
- } while (0)
-
/* This should be used for accessing members of Type[] arrays */
#define mono_type_array_get(arr,index) monotype_cast (mono_array_get ((arr), gpointer, (index)))
MonoReflectionType* mono_reflection_create_runtime_class (MonoReflectionTypeBuilder *tb);
-void mono_reflection_get_dynamic_overrides (MonoClass *klass, MonoMethod ***overrides, int *num_overrides);
+void mono_reflection_get_dynamic_overrides (MonoClass *klass, MonoMethod ***overrides, int *num_overrides, MonoError *error);
void mono_reflection_create_dynamic_method (MonoReflectionDynamicMethod *m);
void mono_reflection_destroy_dynamic_method (MonoReflectionDynamicMethod *mb);
MonoClass*
mono_class_bind_generic_parameters (MonoClass *klass, int type_argc, MonoType **types, gboolean is_dynamic);
MonoType*
-mono_reflection_bind_generic_parameters (MonoReflectionType *type, int type_argc, MonoType **types);
+mono_reflection_bind_generic_parameters (MonoReflectionType *type, int type_argc, MonoType **types, MonoError *error);
MonoReflectionMethod*
mono_reflection_bind_generic_method_parameters (MonoReflectionMethod *method, MonoArray *types);
void
MonoReflectionMarshalAsAttribute* mono_reflection_marshal_as_attribute_from_marshal_spec (MonoDomain *domain, MonoClass *klass, MonoMarshalSpec *spec, MonoError *error);
gpointer
-mono_reflection_lookup_dynamic_token (MonoImage *image, guint32 token, gboolean valid_token, MonoClass **handle_class, MonoGenericContext *context);
+mono_reflection_lookup_dynamic_token (MonoImage *image, guint32 token, gboolean valid_token, MonoClass **handle_class, MonoGenericContext *context, MonoError *error);
gboolean
-mono_reflection_call_is_assignable_to (MonoClass *klass, MonoClass *oklass);
+mono_reflection_call_is_assignable_to (MonoClass *klass, MonoClass *oklass, MonoError *error);
gboolean
mono_reflection_is_valid_dynamic_token (MonoDynamicImage *image, guint32 token);
void
mono_nullable_init (guint8 *buf, MonoObject *value, MonoClass *klass);
+MonoObject *
+mono_value_box_checked (MonoDomain *domain, MonoClass *klass, void* val, MonoError *error);
+
MonoObject*
-mono_nullable_box (guint8 *buf, MonoClass *klass);
+mono_nullable_box (guint8 *buf, MonoClass *klass, MonoError *error);
#ifdef MONO_SMALL_CONFIG
#define MONO_IMT_SIZE 9
void
mono_error_raise_exception (MonoError *target_error);
-void
+gboolean
mono_error_set_pending_exception (MonoError *error);
MonoArray *
MonoObject *
mono_object_clone_checked (MonoObject *obj, MonoError *error);
+MonoObject *
+mono_object_isinst_checked (MonoObject *obj, MonoClass *klass, MonoError *error);
+
+MonoObject *
+mono_object_isinst_mbyref_checked (MonoObject *obj, MonoClass *klass, MonoError *error);
+
MonoString *
mono_string_new_size_checked (MonoDomain *domain, gint32 len, MonoError *error);
klass = mono_class_from_mono_type (type);
if (mono_class_is_nullable (klass))
- return mono_nullable_box (mono_field_get_addr (obj, vtable, field), klass);
+ return mono_nullable_box (mono_field_get_addr (obj, vtable, field), klass, error);
o = mono_object_new_checked (domain, klass, error);
return_val_if_nok (error, NULL);
* mono_nullable_box:
* @buf: The buffer representing the data to be boxed
* @klass: the type to box it as.
+ * @error: set on oerr
*
* Creates a boxed vtype or NULL from the Nullable structure pointed to by
- * @buf.
+ * @buf. On failure returns NULL and sets @error
*/
MonoObject*
-mono_nullable_box (guint8 *buf, MonoClass *klass)
+mono_nullable_box (guint8 *buf, MonoClass *klass, MonoError *error)
{
MONO_REQ_GC_UNSAFE_MODE;
- MonoError error;
-
+ mono_error_init (error);
MonoClass *param_class = klass->cast_class;
mono_class_setup_fields_locking (klass);
g_assert (mono_class_from_mono_type (klass->fields [1].type) == mono_defaults.boolean_class);
if (*(guint8*)(buf + klass->fields [1].offset - sizeof (MonoObject))) {
- MonoObject *o = mono_object_new_checked (mono_domain_get (), param_class, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoObject *o = mono_object_new_checked (mono_domain_get (), param_class, error);
+ return_val_if_nok (error, NULL);
if (param_class->has_references)
mono_gc_wbarrier_value_copy (mono_object_unbox (o), buf + klass->fields [0].offset - sizeof (MonoObject), 1, param_class);
else
* boxed object in the arg array with the copy.
*/
MonoObject *orig = mono_array_get (params, MonoObject*, i);
- MonoObject *copy = mono_value_box (mono_domain_get (), orig->vtable->klass, mono_object_unbox (orig));
+ MonoObject *copy = mono_value_box_checked (mono_domain_get (), orig->vtable->klass, mono_object_unbox (orig), &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
mono_array_setref (params, i, copy);
}
if (!params)
return NULL;
- else
- return mono_value_box (mono_domain_get (), method->klass->cast_class, pa [0]);
+ else {
+ MonoObject *result = mono_value_box_checked (mono_domain_get (), method->klass->cast_class, pa [0], &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ return result;
+ }
}
if (!obj) {
else
o = obj;
} else if (method->klass->valuetype) {
- obj = mono_value_box (mono_domain_get (), method->klass, obj);
+ obj = mono_value_box_checked (mono_domain_get (), method->klass, obj, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
}
if (exc) {
nullable = mono_object_new_checked (mono_domain_get (), method->klass, &error);
mono_error_raise_exception (&error); /* FIXME don't raise here */
- mono_nullable_init ((guint8 *)mono_object_unbox (nullable), mono_value_box (mono_domain_get (), method->klass->cast_class, obj), method->klass);
+ MonoObject *boxed = mono_value_box_checked (mono_domain_get (), method->klass->cast_class, obj, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_nullable_init ((guint8 *)mono_object_unbox (nullable), boxed, method->klass);
obj = mono_object_unbox (nullable);
}
im = mono_class_get_method_from_name (klass, "CreateProxyForType", 1);
if (!im) {
- mono_error_set_generic_error (error, "System", "NotSupportedException", "Linked away.");
+ mono_error_set_not_supported (error, "Linked away.");
return NULL;
}
vtable->domain->create_proxy_for_type_method = im;
MonoObject *
mono_value_box (MonoDomain *domain, MonoClass *klass, gpointer value)
{
- MONO_REQ_GC_UNSAFE_MODE;
-
MonoError error;
+ MonoObject *result = mono_value_box_checked (domain, klass, value, &error);
+ mono_error_cleanup (&error);
+ return result;
+}
+
+/**
+ * mono_value_box_checked:
+ * @domain: the domain of the new object
+ * @class: the class of the value
+ * @value: a pointer to the unboxed data
+ * @error: set on error
+ *
+ * Returns: A newly created object which contains @value. On failure
+ * returns NULL and sets @error.
+ */
+MonoObject *
+mono_value_box_checked (MonoDomain *domain, MonoClass *klass, gpointer value, MonoError *error)
+{
+ MONO_REQ_GC_UNSAFE_MODE;
MonoObject *res;
int size;
MonoVTable *vtable;
+ mono_error_init (error);
+
g_assert (klass->valuetype);
if (mono_class_is_nullable (klass))
- return mono_nullable_box ((guint8 *)value, klass);
+ return mono_nullable_box ((guint8 *)value, klass, error);
vtable = mono_class_vtable (domain, klass);
if (!vtable)
return NULL;
size = mono_class_instance_size (klass);
- res = mono_object_new_alloc_specific_checked (vtable, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ res = mono_object_new_alloc_specific_checked (vtable, error);
+ return_val_if_nok (error, NULL);
size = size - sizeof (MonoObject);
#endif
#endif
if (klass->has_finalize) {
- mono_object_register_finalizer (res, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_object_register_finalizer (res, error);
+ return_val_if_nok (error, NULL);
}
return res;
}
* @obj: an object
* @klass: a pointer to a class
*
- * Returns: @obj if @obj is derived from @klass
+ * Returns: @obj if @obj is derived from @klass or NULL otherwise.
*/
MonoObject *
mono_object_isinst (MonoObject *obj, MonoClass *klass)
{
MONO_REQ_GC_UNSAFE_MODE;
+ MonoError error;
+ MonoObject *result = mono_object_isinst_checked (obj, klass, &error);
+ mono_error_cleanup (&error);
+ return result;
+}
+
+
+/**
+ * mono_object_isinst_checked:
+ * @obj: an object
+ * @klass: a pointer to a class
+ * @error: set on error
+ *
+ * Returns: @obj if @obj is derived from @klass or NULL if it isn't.
+ * On failure returns NULL and sets @error.
+ */
+MonoObject *
+mono_object_isinst_checked (MonoObject *obj, MonoClass *klass, MonoError *error)
+{
+ MONO_REQ_GC_UNSAFE_MODE;
+
+ mono_error_init (error);
+
+ MonoObject *result = NULL;
+
if (!klass->inited)
mono_class_init (klass);
- if (mono_class_is_marshalbyref (klass) || (klass->flags & TYPE_ATTRIBUTE_INTERFACE))
- return mono_object_isinst_mbyref (obj, klass);
+ if (mono_class_is_marshalbyref (klass) || (klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
+ result = mono_object_isinst_mbyref_checked (obj, klass, error);
+ return result;
+ }
if (!obj)
return NULL;
MONO_REQ_GC_UNSAFE_MODE;
MonoError error;
+ MonoObject *result = mono_object_isinst_mbyref_checked (obj, klass, &error);
+ mono_error_cleanup (&error); /* FIXME better API that doesn't swallow the error */
+ return result;
+}
+
+MonoObject *
+mono_object_isinst_mbyref_checked (MonoObject *obj, MonoClass *klass, MonoError *error)
+{
+ MONO_REQ_GC_UNSAFE_MODE;
+
MonoVTable *vt;
+ mono_error_init (error);
+
if (!obj)
return NULL;
im = mono_object_get_virtual_method (rp, im);
g_assert (im);
- pa [0] = mono_type_get_object_checked (domain, &klass->byval_arg, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ pa [0] = mono_type_get_object_checked (domain, &klass->byval_arg, error);
+ return_val_if_nok (error, NULL);
pa [1] = obj;
- res = mono_runtime_invoke_checked (im, rp, pa, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ res = mono_runtime_invoke_checked (im, rp, pa, error);
+ return_val_if_nok (error, NULL);
if (*(MonoBoolean *) mono_object_unbox(res)) {
/* Update the vtable of the remote type, so it can safely cast to this new type */
* @obj: an object
* @klass: a pointer to a class
*
- * Returns: @obj if @obj is derived from @klass, throws an exception otherwise
+ * Returns: @obj if @obj is derived from @klass, returns NULL otherwise.
*/
MonoObject *
mono_object_castclass_mbyref (MonoObject *obj, MonoClass *klass)
{
MONO_REQ_GC_UNSAFE_MODE;
+ MonoError error;
if (!obj) return NULL;
- if (mono_object_isinst_mbyref (obj, klass)) return obj;
-
- mono_raise_exception (mono_exception_from_name (mono_defaults.corlib,
- "System",
- "InvalidCastException"));
+ if (mono_object_isinst_mbyref_checked (obj, klass, &error)) return obj;
+ mono_error_cleanup (&error);
return NULL;
}
mono_ldstr (MonoDomain *domain, MonoImage *image, guint32 idx)
{
MONO_REQ_GC_UNSAFE_MODE;
+ MonoError error;
if (image->dynamic) {
- MonoString *str = (MonoString *)mono_lookup_dynamic_token (image, MONO_TOKEN_STRING | idx, NULL);
+ MonoString *str = (MonoString *)mono_lookup_dynamic_token (image, MONO_TOKEN_STRING | idx, NULL, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
return str;
} else {
if (!mono_verifier_verify_string_signature (image, idx, NULL))
if (!im) {
im = mono_class_get_method_from_name (mono_defaults.real_proxy_class, "PrivateInvoke", 4);
if (!im) {
- mono_error_set_generic_error (error, "System", "NotSupportedException", "Linked away.");
+ mono_error_set_not_supported (error, "Linked away.");
return NULL;
}
real_proxy->vtable->domain->private_invoke_method = im;
klass = mono_class_from_mono_type (sig->params [i]);
- if (klass->valuetype)
- arg = mono_value_box (domain, klass, vpos);
- else
+ if (klass->valuetype) {
+ arg = mono_value_box_checked (domain, klass, vpos, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ } else
arg = *((MonoObject **)vpos);
mono_array_setref (msg->args, i, arg);
mono_raise_exception (mono_get_exception_not_supported ("Linked away."));
}
- if (field_class->valuetype)
- arg = mono_value_box (domain, field_class, val);
- else
+ if (field_class->valuetype) {
+ arg = mono_value_box_checked (domain, field_class, val, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ } else
arg = *((MonoObject **)val);
MONO_API MonoString *
mono_object_to_string (MonoObject *obj, MonoObject **exc);
+MONO_RT_EXTERNAL_ONLY
MONO_API MonoObject *
mono_value_box (MonoDomain *domain, MonoClass *klass, void* val);
MONO_API MonoObject *
mono_object_clone (MonoObject *obj);
+MONO_RT_EXTERNAL_ONLY
MONO_API MonoObject *
mono_object_isinst (MonoObject *obj, MonoClass *klass);
+MONO_RT_EXTERNAL_ONLY
MONO_API MonoObject *
mono_object_isinst_mbyref (MonoObject *obj, MonoClass *klass);
+MONO_RT_EXTERNAL_ONLY
MONO_API MonoObject *
mono_object_castclass_mbyref (MonoObject *obj, MonoClass *klass);
MonoArray*
mono_reflection_get_custom_attrs_data_checked (MonoObject *obj, MonoError *error);
+MonoArray*
+mono_reflection_get_custom_attrs_blob_checked (MonoReflectionAssembly *assembly, MonoObject *ctor, MonoArray *ctorArgs, MonoArray *properties, MonoArray *propValues, MonoArray *fields, MonoArray* fieldValues, MonoError *error);
+
MonoCustomAttrInfo*
mono_custom_attrs_from_index_checked (MonoImage *image, uint32_t idx, MonoError *error);
MonoCustomAttrInfo*
static guint32 mono_image_get_ctorbuilder_token (MonoDynamicImage *assembly, MonoReflectionCtorBuilder *cb, MonoError *error);
static guint32 mono_image_get_sighelper_token (MonoDynamicImage *assembly, MonoReflectionSigHelper *helper, MonoError *error);
static gboolean ensure_runtime_vtable (MonoClass *klass, MonoError *error);
-static gpointer resolve_object (MonoImage *image, MonoObject *obj, MonoClass **handle_class, MonoGenericContext *context);
+static gpointer resolve_object (MonoImage *image, MonoObject *obj, MonoClass **handle_class, MonoGenericContext *context, MonoError *error);
static guint32 mono_image_get_methodref_token_for_methodbuilder (MonoDynamicImage *assembly, MonoReflectionMethodBuilder *method, MonoError *error);
static guint32 encode_generic_method_sig (MonoDynamicImage *assembly, MonoGenericContext *context);
static gpointer register_assembly (MonoDomain *domain, MonoReflectionAssembly *res, MonoAssembly *assembly);
static gboolean is_sre_method_on_tb_inst (MonoClass *klass);
static gboolean is_sre_ctor_on_tb_inst (MonoClass *klass);
+static gboolean type_is_reference (MonoType *type);
+
static guint32 mono_image_get_methodspec_token (MonoDynamicImage *assembly, MonoMethod *method);
static guint32 mono_image_get_inflated_method_token (MonoDynamicImage *assembly, MonoMethod *m);
static MonoMethod * inflate_method (MonoReflectionType *type, MonoObject *obj, MonoError *error);
/* Check for user defined reflection objects */
/* TypeDelegator is the only corlib type which doesn't look like a MonoReflectionType */
if (klass->image != mono_defaults.corlib || (strcmp (klass->name, "TypeDelegator") == 0)) {
- mono_error_set_generic_error (error, "System", "NotSupportedException", "User defined subclasses of System.Type are not yet supported");
+ mono_error_set_not_supported (error, "User defined subclasses of System.Type are not yet supported");
return 0;
}
{
MonoError error;
MonoReflectionMethodBody *result = mono_method_body_get_object_checked (domain, method, &error);
- mono_error_cleanup (&error); /* FIXME new API that doesn't swallow the error */
+ mono_error_cleanup (&error); /* FIXME better API that doesn't swallow the error */
return result;
}
/* for compatibility with .net */
if (method_is_dynamic (method)) {
- mono_error_set_exception_instance (error, mono_get_exception_invalid_operation (NULL));
+ mono_error_set_generic_error (error, "System", "InvalidOperationException", "");
return NULL;
}
local_var_sig_token = 0; //FIXME
ret = (MonoReflectionMethodBody*)mono_object_new_checked (domain, mono_class_get_method_body_class (), error);
- return_val_if_nok (error, NULL);
+ if (!is_ok (error))
+ goto fail;
ret->init_locals = header->init_locals;
ret->max_stack = header->max_stack;
MONO_OBJECT_SETREF (ret, locals, mono_array_new_cached (domain, mono_class_get_local_variable_info_class (), header->num_locals));
for (i = 0; i < header->num_locals; ++i) {
MonoReflectionLocalVariableInfo *info = (MonoReflectionLocalVariableInfo*)mono_object_new_checked (domain, mono_class_get_local_variable_info_class (), error);
- return_val_if_nok (error, NULL);
+ if (!is_ok (error))
+ goto fail;
rt = mono_type_get_object_checked (domain, header->locals [i], error);
- return_val_if_nok (error, NULL);
+ if (!is_ok (error))
+ goto fail;
MONO_OBJECT_SETREF (info, local_type, rt);
MONO_OBJECT_SETREF (ret, clauses, mono_array_new_cached (domain, mono_class_get_exception_handling_clause_class (), header->num_clauses));
for (i = 0; i < header->num_clauses; ++i) {
MonoReflectionExceptionHandlingClause *info = (MonoReflectionExceptionHandlingClause*)mono_object_new_checked (domain, mono_class_get_exception_handling_clause_class (), error);
- return_val_if_nok (error, NULL);
+ if (!is_ok (error))
+ goto fail;
MonoExceptionClause *clause = &header->clauses [i];
info->flags = clause->flags;
info->filter_offset = clause->data.filter_offset;
else if (clause->data.catch_class) {
rt = mono_type_get_object_checked (mono_domain_get (), &clause->data.catch_class->byval_arg, error);
- return_val_if_nok (error, NULL);
+ if (!is_ok (error))
+ goto fail;
MONO_OBJECT_SETREF (info, catch_type, rt);
}
mono_metadata_free_mh (header);
CACHE_OBJECT (MonoReflectionMethodBody *, method, ret, NULL);
return ret;
+
+fail:
+ mono_metadata_free_mh (header);
+ return NULL;
}
/**
return NULL;
instance = mono_reflection_bind_generic_parameters (
- the_type, info->type_arguments->len, type_args);
+ the_type, info->type_arguments->len, type_args, error);
g_free (type_args);
if (!instance)
} else if (strcmp (klass->name, "Assembly") == 0 || strcmp (klass->name, "MonoAssembly") == 0) {
token = mono_metadata_make_token (MONO_TABLE_ASSEMBLY, 1);
} else {
- mono_error_set_generic_error (error, "System", "NotImplementedException",
- "MetadataToken is not supported for type '%s.%s'", klass->name_space, klass->name);
+ mono_error_set_not_implemented (error, "MetadataToken is not supported for type '%s.%s'",
+ klass->name_space, klass->name);
return 0;
}
return NULL;
}
+static MonoObject*
+load_cattr_value_boxed (MonoDomain *domain, MonoImage *image, MonoType *t, const char* p, const char** end, MonoError *error)
+{
+ mono_error_init (error);
+
+ gboolean is_ref = type_is_reference (t);
+
+ void *val = load_cattr_value (image, t, p, end, error);
+ if (!is_ok (error)) {
+ if (is_ref)
+ g_free (val);
+ return NULL;
+ }
+
+ if (is_ref)
+ return (MonoObject*)val;
+
+ MonoObject *boxed = mono_value_box_checked (domain, mono_class_from_mono_type (t), val, error);
+ g_free (val);
+ return boxed;
+}
+
static MonoObject*
create_cattr_typed_arg (MonoType *t, MonoObject *val, MonoError *error)
{
p += 2;
for (i = 0; i < mono_method_signature (method)->param_count; ++i) {
MonoObject *obj;
- void *val;
-
- val = load_cattr_value (image, mono_method_signature (method)->params [i], p, &p, error);
- if (!mono_error_ok (error)) {
- if (!type_is_reference (mono_method_signature (method)->params [i]))
- g_free (val);
- return;
- }
- obj = (MonoObject *)(type_is_reference (mono_method_signature (method)->params [i]) ?
- val : mono_value_box (domain, mono_class_from_mono_type (mono_method_signature (method)->params [i]), val));
+ obj = load_cattr_value_boxed (domain, image, mono_method_signature (method)->params [i], p, &p, error);
+ return_if_nok (error);
mono_array_setref (typedargs, i, obj);
-
- if (!type_is_reference (mono_method_signature (method)->params [i]))
- g_free (val);
}
named = p;
if (named_type == 0x53) {
MonoObject *obj;
MonoClassField *field = mono_class_get_field_from_name (attrklass, name);
- void *val;
if (!field) {
g_free (name);
arginfo [j].type = field->type;
arginfo [j].field = field;
- val = load_cattr_value (image, field->type, named, &named, error);
- if (!mono_error_ok (error)) {
- if (!type_is_reference (field->type))
- g_free (val);
+ obj = load_cattr_value_boxed (domain, image, field->type, named, &named, error);
+ if (!is_ok (error)) {
g_free (name);
return;
}
-
- obj = (MonoObject *)(type_is_reference (field->type) ? val : mono_value_box (domain, mono_class_from_mono_type (field->type), val));
mono_array_setref (namedargs, j, obj);
- if (!type_is_reference (field->type))
- g_free (val);
+
} else if (named_type == 0x54) {
MonoObject *obj;
MonoType *prop_type;
MonoProperty *prop = mono_class_get_property_from_name (attrklass, name);
- void *val;
if (!prop || !prop->set) {
g_free (name);
arginfo [j].type = prop_type;
arginfo [j].prop = prop;
- val = load_cattr_value (image, prop_type, named, &named, error);
- if (!mono_error_ok (error)) {
- if (!type_is_reference (prop_type))
- g_free (val);
+ obj = load_cattr_value_boxed (domain, image, prop_type, named, &named, error);
+ if (!is_ok (error)) {
g_free (name);
return;
}
-
- obj = (MonoObject *)(type_is_reference (prop_type) ? val : mono_value_box (domain, mono_class_from_mono_type (prop_type), val));
mono_array_setref (namedargs, j, obj);
- if (!type_is_reference (prop_type))
- g_free (val);
}
g_free (name);
}
#endif
else {
char *type_name = mono_type_get_full_name (member_class);
- mono_error_set_generic_error (error, "System", "NotSupportedException",
+ mono_error_set_not_supported (error,
"Custom attributes on a ParamInfo with member %s are not supported",
type_name);
g_free (type_name);
}
}
- res = mono_reflection_bind_generic_parameters (gclass->generic_type, count, types);
+ res = mono_reflection_bind_generic_parameters (gclass->generic_type, count, types, error);
g_free (types);
g_assert (res);
gclass->type.type = res;
mono_error_set_pending_exception (&error);
}
-void
-mono_reflection_register_with_runtime (MonoReflectionType *type)
+static gboolean
+reflection_register_with_runtime (MonoReflectionType *type, MonoError *error)
{
- MonoError error;
- MonoType *res = mono_reflection_type_get_handle (type, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
MonoDomain *domain = mono_object_domain ((MonoObject*)type);
MonoClass *klass;
- if (!res)
- mono_raise_exception (mono_get_exception_argument (NULL, "Invalid generic instantiation, one or more arguments are not proper user types"));
+ mono_error_init (error);
+
+ MonoType *res = mono_reflection_type_get_handle (type, error);
+
+ if (!res && is_ok (error)) {
+ mono_error_set_argument (error, NULL, "Invalid generic instantiation, one or more arguments are not proper user types");
+ }
+ return_val_if_nok (error, FALSE);
klass = mono_class_from_mono_type (res);
}
mono_domain_unlock (domain);
mono_loader_unlock ();
+
+ return TRUE;
+}
+
+void
+mono_reflection_register_with_runtime (MonoReflectionType *type)
+{
+ MonoError error;
+ (void) reflection_register_with_runtime (type, &error);
+ mono_error_set_pending_exception (&error);
}
/**
* LOCKING: Assumes the loader lock is held.
*/
static MonoMethodSignature*
-parameters_to_signature (MonoImage *image, MonoArray *parameters) {
- MonoError error;
+parameters_to_signature (MonoImage *image, MonoArray *parameters, MonoError *error) {
MonoMethodSignature *sig;
int count, i;
+ mono_error_init (error);
+
count = parameters? mono_array_length (parameters): 0;
sig = (MonoMethodSignature *)image_g_malloc0 (image, MONO_SIZEOF_METHOD_SIGNATURE + sizeof (MonoType*) * count);
sig->param_count = count;
sig->sentinelpos = -1; /* FIXME */
for (i = 0; i < count; ++i) {
- sig->params [i] = mono_type_array_get_and_resolve (parameters, i, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ sig->params [i] = mono_type_array_get_and_resolve (parameters, i, error);
+ if (!is_ok (error)) {
+ image_g_free (image, sig);
+ return NULL;
+ }
}
return sig;
}
* LOCKING: Assumes the loader lock is held.
*/
static MonoMethodSignature*
-ctor_builder_to_signature (MonoImage *image, MonoReflectionCtorBuilder *ctor) {
+ctor_builder_to_signature (MonoImage *image, MonoReflectionCtorBuilder *ctor, MonoError *error) {
MonoMethodSignature *sig;
- sig = parameters_to_signature (image, ctor->parameters);
+ mono_error_init (error);
+
+ sig = parameters_to_signature (image, ctor->parameters, error);
+ return_val_if_nok (error, NULL);
sig->hasthis = ctor->attrs & METHOD_ATTRIBUTE_STATIC? 0: 1;
sig->ret = &mono_defaults.void_class->byval_arg;
return sig;
* LOCKING: Assumes the loader lock is held.
*/
static MonoMethodSignature*
-method_builder_to_signature (MonoImage *image, MonoReflectionMethodBuilder *method) {
- MonoError error;
+method_builder_to_signature (MonoImage *image, MonoReflectionMethodBuilder *method, MonoError *error) {
MonoMethodSignature *sig;
- sig = parameters_to_signature (image, method->parameters);
+ mono_error_init (error);
+
+ sig = parameters_to_signature (image, method->parameters, error);
+ return_val_if_nok (error, NULL);
sig->hasthis = method->attrs & METHOD_ATTRIBUTE_STATIC? 0: 1;
if (method->rtype) {
- sig->ret = mono_reflection_type_get_handle ((MonoReflectionType*)method->rtype, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ sig->ret = mono_reflection_type_get_handle ((MonoReflectionType*)method->rtype, error);
+ if (!is_ok (error)) {
+ image_g_free (image, sig);
+ return NULL;
+ }
} else {
sig->ret = &mono_defaults.void_class->byval_arg;
}
}
static MonoMethodSignature*
-dynamic_method_to_signature (MonoReflectionDynamicMethod *method) {
- MonoError error;
+dynamic_method_to_signature (MonoReflectionDynamicMethod *method, MonoError *error) {
MonoMethodSignature *sig;
- sig = parameters_to_signature (NULL, method->parameters);
+ mono_error_init (error);
+
+ sig = parameters_to_signature (NULL, method->parameters, error);
+ return_val_if_nok (error, NULL);
sig->hasthis = method->attrs & METHOD_ATTRIBUTE_STATIC? 0: 1;
if (method->rtype) {
- sig->ret = mono_reflection_type_get_handle (method->rtype, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ sig->ret = mono_reflection_type_get_handle (method->rtype, error);
+ if (!is_ok (error)) {
+ g_free (sig);
+ return NULL;
+ }
} else {
sig->ret = &mono_defaults.void_class->byval_arg;
}
}
static void
-get_prop_name_and_type (MonoObject *prop, char **name, MonoType **type)
+get_prop_name_and_type (MonoObject *prop, char **name, MonoType **type, MonoError *error)
{
- MonoError error;
+ mono_error_init (error);
MonoClass *klass = mono_object_class (prop);
if (strcmp (klass->name, "PropertyBuilder") == 0) {
MonoReflectionPropertyBuilder *pb = (MonoReflectionPropertyBuilder *)prop;
*name = mono_string_to_utf8 (pb->name);
- *type = mono_reflection_type_get_handle ((MonoReflectionType*)pb->type, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ *type = mono_reflection_type_get_handle ((MonoReflectionType*)pb->type, error);
} else {
MonoReflectionProperty *p = (MonoReflectionProperty *)prop;
*name = g_strdup (p->property->name);
}
static void
-get_field_name_and_type (MonoObject *field, char **name, MonoType **type)
+get_field_name_and_type (MonoObject *field, char **name, MonoType **type, MonoError *error)
{
- MonoError error;
+ mono_error_init (error);
MonoClass *klass = mono_object_class (field);
if (strcmp (klass->name, "FieldBuilder") == 0) {
MonoReflectionFieldBuilder *fb = (MonoReflectionFieldBuilder *)field;
*name = mono_string_to_utf8 (fb->name);
- *type = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ *type = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, error);
} else {
MonoReflectionField *f = (MonoReflectionField *)field;
*name = g_strdup (mono_field_get_name (f->field));
type = mono_reflection_type_get_underlying_system_type (type, error);
return_val_if_nok (error, NULL);
if (is_usertype (type)) {
- mono_error_set_generic_error (error, "System", "NotSupportedException", "User defined subclasses of System.Type are not yet supported22");
+ mono_error_set_not_supported (error, "User defined subclasses of System.Type are not yet supported22");
return NULL;
}
}
return type;
}
-/*
+/**
+ * encode_cattr_value:
* Encode a value in a custom attribute stream of bytes.
* The value to encode is either supplied as an object in argument val
* (valuetypes are boxed), or as a pointer to the data in the
* @buflen contains the size of the buffer and is used to return the new buffer size
* if this needs to be realloced.
* @retbuffer and @retp return the start and the position of the buffer
+ * @error set on error.
*/
static void
-encode_cattr_value (MonoAssembly *assembly, char *buffer, char *p, char **retbuffer, char **retp, guint32 *buflen, MonoType *type, MonoObject *arg, char *argval)
+encode_cattr_value (MonoAssembly *assembly, char *buffer, char *p, char **retbuffer, char **retp, guint32 *buflen, MonoType *type, MonoObject *arg, char *argval, MonoError *error)
{
- MonoError error;
MonoTypeEnum simple_type;
+ mono_error_init (error);
if ((p-buffer) + 10 >= *buflen) {
char *newbuf;
*buflen *= 2;
break;
}
handle_type:
- arg_type = mono_reflection_type_get_handle ((MonoReflectionType*)arg, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ arg_type = mono_reflection_type_get_handle ((MonoReflectionType*)arg, error);
+ return_if_nok (error);
+
str = type_get_qualified_name (arg_type, NULL);
slen = strlen (str);
if ((p-buffer) + 10 + slen >= *buflen) {
char *elptr = mono_array_addr ((MonoArray*)arg, char, 0);
int elsize = mono_class_array_element_size (arg_eclass);
for (i = 0; i < len; ++i) {
- encode_cattr_value (assembly, buffer, p, &buffer, &p, buflen, &arg_eclass->byval_arg, NULL, elptr);
+ encode_cattr_value (assembly, buffer, p, &buffer, &p, buflen, &arg_eclass->byval_arg, NULL, elptr, error);
+ return_if_nok (error);
elptr += elsize;
}
} else if (eclass->valuetype && arg_eclass->valuetype) {
char *elptr = mono_array_addr ((MonoArray*)arg, char, 0);
int elsize = mono_class_array_element_size (eclass);
for (i = 0; i < len; ++i) {
- encode_cattr_value (assembly, buffer, p, &buffer, &p, buflen, &eclass->byval_arg, NULL, elptr);
+ encode_cattr_value (assembly, buffer, p, &buffer, &p, buflen, &eclass->byval_arg, NULL, elptr, error);
+ return_if_nok (error);
elptr += elsize;
}
} else {
for (i = 0; i < len; ++i) {
- encode_cattr_value (assembly, buffer, p, &buffer, &p, buflen, &eclass->byval_arg, mono_array_get ((MonoArray*)arg, MonoObject*, i), NULL);
+ encode_cattr_value (assembly, buffer, p, &buffer, &p, buflen, &eclass->byval_arg, mono_array_get ((MonoArray*)arg, MonoObject*, i), NULL, error);
+ return_if_nok (error);
}
}
break;
klass = mono_object_class (arg);
- if (mono_object_isinst (arg, mono_defaults.systemtype_class)) {
+ if (mono_object_isinst_checked (arg, mono_defaults.systemtype_class, error)) {
*p++ = 0x50;
goto handle_type;
- } else if (klass->enumtype) {
+ } else {
+ return_if_nok (error);
+ }
+
+ if (klass->enumtype) {
*p++ = 0x55;
} else if (klass == mono_defaults.string_class) {
simple_type = MONO_TYPE_STRING;
*p++ = 0x51;
else
*p++ = klass->element_class->byval_arg.type;
- encode_cattr_value (assembly, buffer, p, &buffer, &p, buflen, &klass->byval_arg, arg, NULL);
+ encode_cattr_value (assembly, buffer, p, &buffer, &p, buflen, &klass->byval_arg, arg, NULL, error);
+ return_if_nok (error);
break;
} else if (klass->byval_arg.type >= MONO_TYPE_BOOLEAN && klass->byval_arg.type <= MONO_TYPE_R8) {
*p++ = simple_type = klass->byval_arg.type;
#ifndef DISABLE_REFLECTION_EMIT
static void
-encode_named_val (MonoReflectionAssembly *assembly, char *buffer, char *p, char **retbuffer, char **retp, guint32 *buflen, MonoType *type, char *name, MonoObject *value)
+encode_named_val (MonoReflectionAssembly *assembly, char *buffer, char *p, char **retbuffer, char **retp, guint32 *buflen, MonoType *type, char *name, MonoObject *value, MonoError *error)
{
int len;
+
+ mono_error_init (error);
+
/* Preallocate a large enough buffer */
if (type->type == MONO_TYPE_VALUETYPE && type->data.klass->enumtype) {
char *str = type_get_qualified_name (type, NULL);
mono_metadata_encode_value (len, p, &p);
memcpy (p, name, len);
p += len;
- encode_cattr_value (assembly->assembly, buffer, p, &buffer, &p, buflen, type, value, NULL);
+ encode_cattr_value (assembly->assembly, buffer, p, &buffer, &p, buflen, type, value, NULL, error);
+ return_if_nok (error);
*retp = p;
*retbuffer = buffer;
}
-/*
+/**
* mono_reflection_get_custom_attrs_blob:
* @ctor: custom attribute constructor
* @ctorArgs: arguments o the constructor
MonoArray*
mono_reflection_get_custom_attrs_blob (MonoReflectionAssembly *assembly, MonoObject *ctor, MonoArray *ctorArgs, MonoArray *properties, MonoArray *propValues, MonoArray *fields, MonoArray* fieldValues)
{
- MonoArray *result;
+ MonoError error;
+ MonoArray *result = mono_reflection_get_custom_attrs_blob_checked (assembly, ctor, ctorArgs, properties, propValues, fields, fieldValues, &error);
+ mono_error_cleanup (&error); /* FIXME better API that doesn't swallow the error */
+ return result;
+}
+
+/**
+ * mono_reflection_get_custom_attrs_blob_checked:
+ * @ctor: custom attribute constructor
+ * @ctorArgs: arguments o the constructor
+ * @properties:
+ * @propValues:
+ * @fields:
+ * @fieldValues:
+ * @error: set on error
+ *
+ * Creates the blob of data that needs to be saved in the metadata and that represents
+ * the custom attributed described by @ctor, @ctorArgs etc.
+ * Returns: a Byte array representing the blob of data. On failure returns NULL and sets @error.
+ */
+MonoArray*
+mono_reflection_get_custom_attrs_blob_checked (MonoReflectionAssembly *assembly, MonoObject *ctor, MonoArray *ctorArgs, MonoArray *properties, MonoArray *propValues, MonoArray *fields, MonoArray* fieldValues, MonoError *error)
+{
+ MonoArray *result = NULL;
MonoMethodSignature *sig;
MonoObject *arg;
char *buffer, *p;
guint32 buflen, i;
+ mono_error_init (error);
+
if (strcmp (ctor->vtable->klass->name, "MonoCMethod")) {
/* sig is freed later so allocate it in the heap */
- sig = ctor_builder_to_signature (NULL, (MonoReflectionCtorBuilder*)ctor);
+ sig = ctor_builder_to_signature (NULL, (MonoReflectionCtorBuilder*)ctor, error);
+ if (!is_ok (error)) {
+ g_free (sig);
+ return NULL;
+ }
} else {
sig = mono_method_signature (((MonoReflectionMethod*)ctor)->method);
}
*p++ = 0;
for (i = 0; i < sig->param_count; ++i) {
arg = mono_array_get (ctorArgs, MonoObject*, i);
- encode_cattr_value (assembly->assembly, buffer, p, &buffer, &p, &buflen, sig->params [i], arg, NULL);
+ encode_cattr_value (assembly->assembly, buffer, p, &buffer, &p, &buflen, sig->params [i], arg, NULL, error);
+ if (!is_ok (error)) goto leave;
}
i = 0;
if (properties)
char *pname;
prop = (MonoObject *)mono_array_get (properties, gpointer, i);
- get_prop_name_and_type (prop, &pname, &ptype);
+ get_prop_name_and_type (prop, &pname, &ptype, error);
+ if (!is_ok (error)) goto leave;
*p++ = 0x54; /* PROPERTY signature */
- encode_named_val (assembly, buffer, p, &buffer, &p, &buflen, ptype, pname, (MonoObject*)mono_array_get (propValues, gpointer, i));
+ encode_named_val (assembly, buffer, p, &buffer, &p, &buflen, ptype, pname, (MonoObject*)mono_array_get (propValues, gpointer, i), error);
g_free (pname);
+ if (!is_ok (error)) goto leave;
}
}
char *fname;
field = (MonoObject *)mono_array_get (fields, gpointer, i);
- get_field_name_and_type (field, &fname, &ftype);
+ get_field_name_and_type (field, &fname, &ftype, error);
+ if (!is_ok (error)) goto leave;
*p++ = 0x53; /* FIELD signature */
- encode_named_val (assembly, buffer, p, &buffer, &p, &buflen, ftype, fname, (MonoObject*)mono_array_get (fieldValues, gpointer, i));
+ encode_named_val (assembly, buffer, p, &buffer, &p, &buflen, ftype, fname, (MonoObject*)mono_array_get (fieldValues, gpointer, i), error);
g_free (fname);
+ if (!is_ok (error)) goto leave;
}
}
result = mono_array_new (mono_domain_get (), mono_defaults.byte_class, buflen);
p = mono_array_addr (result, char, 0);
memcpy (p, buffer, buflen);
+leave:
g_free (buffer);
if (strcmp (ctor->vtable->klass->name, "MonoCMethod"))
g_free (sig);
return result;
}
-/*
- * mono_reflection_setup_internal_class:
+/**
+ * reflection_setup_internal_class:
* @tb: a TypeBuilder object
+ * @error: set on error
*
* Creates a MonoClass that represents the TypeBuilder.
* This is a trick that lets us simplify a lot of reflection code
* (and will allow us to support Build and Run assemblies easier).
+ *
+ * Returns TRUE on success. On failure, returns FALSE and sets @error.
*/
-void
-mono_reflection_setup_internal_class (MonoReflectionTypeBuilder *tb)
+static gboolean
+reflection_setup_internal_class (MonoReflectionTypeBuilder *tb, MonoError *error)
{
- MonoError error;
MonoClass *klass, *parent;
- RESOLVE_TYPE (tb->parent, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_error_init (error);
+ RESOLVE_TYPE (tb->parent, error);
+ return_val_if_nok (error, FALSE);
mono_loader_lock ();
if (tb->parent) {
- MonoType *parent_type = mono_reflection_type_get_handle ((MonoReflectionType*)tb->parent, &error);
- if (!is_ok (&error)) {
+ MonoType *parent_type = mono_reflection_type_get_handle ((MonoReflectionType*)tb->parent, error);
+ if (!is_ok (error)) {
mono_loader_unlock ();
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ return FALSE;
}
/* check so we can compile corlib correctly */
if (strcmp (mono_object_class (tb->parent)->name, "TypeBuilder") == 0) {
mono_class_setup_parent (klass, parent);
mono_class_setup_mono_type (klass);
mono_loader_unlock ();
- return;
+ return TRUE;
}
klass = (MonoClass *)mono_image_alloc0 (&tb->module->dynamic_image->image, sizeof (MonoClass));
klass->image = &tb->module->dynamic_image->image;
klass->inited = 1; /* we lie to the runtime */
- klass->name = mono_string_to_utf8_image (klass->image, tb->name, &error);
- if (!mono_error_ok (&error))
+ klass->name = mono_string_to_utf8_image (klass->image, tb->name, error);
+ if (!is_ok (error))
goto failure;
- klass->name_space = mono_string_to_utf8_image (klass->image, tb->nspace, &error);
- if (!mono_error_ok (&error))
+ klass->name_space = mono_string_to_utf8_image (klass->image, tb->nspace, error);
+ if (!is_ok (error))
goto failure;
klass->type_token = MONO_TOKEN_TYPE_DEF | tb->table_idx;
klass->flags = tb->attrs;
if (tb->nesting_type) {
g_assert (tb->nesting_type->type);
- MonoType *nesting_type = mono_reflection_type_get_handle (tb->nesting_type, &error);
- if (!is_ok (&error)) goto failure;
+ MonoType *nesting_type = mono_reflection_type_get_handle (tb->nesting_type, error);
+ if (!is_ok (error)) goto failure;
klass->nested_in = mono_class_from_mono_type (nesting_type);
}
mono_profiler_class_loaded (klass, MONO_PROFILE_OK);
mono_loader_unlock ();
- return;
+ return TRUE;
failure:
mono_loader_unlock ();
- mono_error_raise_exception (&error);
+ return FALSE;
+}
+
+/**
+ * mono_reflection_setup_internal_class:
+ * @tb: a TypeBuilder object
+ *
+ * (icall)
+ * Creates a MonoClass that represents the TypeBuilder.
+ * This is a trick that lets us simplify a lot of reflection code
+ * (and will allow us to support Build and Run assemblies easier).
+ *
+ */
+void
+mono_reflection_setup_internal_class (MonoReflectionTypeBuilder *tb)
+{
+ MonoError error;
+ (void) reflection_setup_internal_class (tb, &error);
+ mono_error_set_pending_exception (&error);
}
/*
klass->generic_container->context.class_inst = mono_get_shared_generic_inst (klass->generic_container);
}
-/*
- * mono_reflection_create_internal_class:
+/**
+ * reflection_create_internal_class:
* @tb: a TypeBuilder object
+ * @error: set on error
*
* Actually create the MonoClass that is associated with the TypeBuilder.
+ * On success returns TRUE, on failure returns FALSE and sets @error.
+ *
*/
-void
-mono_reflection_create_internal_class (MonoReflectionTypeBuilder *tb)
+static gboolean
+reflection_create_internal_class (MonoReflectionTypeBuilder *tb, MonoError *error)
{
- MonoError error;
+
MonoClass *klass;
+ mono_error_init (error);
klass = mono_class_from_mono_type (tb->type.type);
mono_loader_lock ();
fb = mono_array_get (tb->fields, MonoReflectionFieldBuilder*, 0);
- MonoType *field_type = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, &error);
- if (!is_ok (&error)) {
+ MonoType *field_type = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, error);
+ if (!is_ok (error)) {
mono_loader_unlock ();
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ return FALSE;
}
if (!mono_type_is_valid_enum_basetype (field_type)) {
mono_loader_unlock ();
- return;
+ return TRUE;
}
- enum_basetype = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, &error);
- if (!is_ok (&error)) {
+ enum_basetype = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, error);
+ if (!is_ok (error)) {
mono_loader_unlock ();
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ return FALSE;
}
klass->element_class = mono_class_from_mono_type (enum_basetype);
if (!klass->element_class)
mono_class_setup_vtable_general (klass, NULL, 0, NULL);
}
mono_loader_unlock ();
+ return TRUE;
+}
+
+/**
+ * mono_reflection_create_internal_class:
+ * @tb: a TypeBuilder object
+ *
+ * (icall)
+ * Actually create the MonoClass that is associated with the TypeBuilder.
+ */
+void
+mono_reflection_create_internal_class (MonoReflectionTypeBuilder *tb)
+{
+ MonoError error;
+ (void) reflection_create_internal_class (tb, &error);
+ mono_error_set_pending_exception (&error);
}
static MonoMarshalSpec*
mono_marshal_spec_from_builder (MonoImage *image, MonoAssembly *assembly,
- MonoReflectionMarshal *minfo)
+ MonoReflectionMarshal *minfo, MonoError *error)
{
- MonoError error;
MonoMarshalSpec *res;
+ mono_error_init (error);
+
res = image_g_new0 (image, MonoMarshalSpec, 1);
res->native = (MonoMarshalNative)minfo->type;
case MONO_NATIVE_CUSTOM:
if (minfo->marshaltyperef) {
- MonoType *marshaltyperef = mono_reflection_type_get_handle ((MonoReflectionType*)minfo->marshaltyperef, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *marshaltyperef = mono_reflection_type_get_handle ((MonoReflectionType*)minfo->marshaltyperef, error);
+ if (!is_ok (error)) {
+ image_g_free (image, res);
+ return NULL;
+ }
res->data.custom_data.custom_name =
type_get_fully_qualified_name (marshaltyperef);
}
if (specs == NULL)
specs = image_g_new0 (image, MonoMarshalSpec*, sig->param_count + 1);
specs [pb->position] =
- mono_marshal_spec_from_builder (image, klass->image->assembly, pb->marshal_info);
+ mono_marshal_spec_from_builder (image, klass->image->assembly, pb->marshal_info, &error);
+ if (!is_ok (&error)) {
+ mono_loader_unlock ();
+ image_g_free (image, specs);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ }
}
}
}
MonoMethodSignature *sig;
mono_loader_lock ();
- sig = ctor_builder_to_signature (klass->image, mb);
+ g_assert (klass->image != NULL);
+ sig = ctor_builder_to_signature (klass->image, mb, error);
mono_loader_unlock ();
+ return_val_if_nok (error, NULL);
if (!reflection_methodbuilder_from_ctor_builder (&rmb, mb, error))
return NULL;
mono_error_init (error);
mono_loader_lock ();
- sig = method_builder_to_signature (klass->image, mb);
+ g_assert (klass->image != NULL);
+ sig = method_builder_to_signature (klass->image, mb, error);
mono_loader_unlock ();
+ return_val_if_nok (error, NULL);
if (!reflection_methodbuilder_from_method_builder (&rmb, mb, error))
return NULL;
}
static MonoClassField*
-fieldbuilder_to_mono_class_field (MonoClass *klass, MonoReflectionFieldBuilder* fb)
+fieldbuilder_to_mono_class_field (MonoClass *klass, MonoReflectionFieldBuilder* fb, MonoError *error)
{
MonoClassField *field;
MonoType *custom;
- MonoError error;
+
+ mono_error_init (error);
field = g_new0 (MonoClassField, 1);
- field->name = mono_string_to_utf8_image (klass->image, fb->name, &error);
- g_assert (mono_error_ok (&error));
+ field->name = mono_string_to_utf8_image (klass->image, fb->name, error);
+ mono_error_assert_ok (error);
if (fb->attrs || fb->modreq || fb->modopt) {
- MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, &error);
- if (!is_ok (&error)) {
+ MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, error);
+ if (!is_ok (error)) {
g_free (field);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ return NULL;
}
field->type = mono_metadata_type_dup (NULL, type);
field->type->attrs = fb->attrs;
g_assert (image_is_dynamic (klass->image));
- custom = add_custom_modifiers ((MonoDynamicImage*)klass->image, field->type, fb->modreq, fb->modopt, &error);
+ custom = add_custom_modifiers ((MonoDynamicImage*)klass->image, field->type, fb->modreq, fb->modopt, error);
g_free (field->type);
- if (!is_ok (&error)) {
+ if (!is_ok (error)) {
g_free (field);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ return NULL;
}
field->type = mono_metadata_type_dup (klass->image, custom);
g_free (custom);
} else {
- field->type = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, &error);
- if (!is_ok (&error)) {
+ field->type = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, error);
+ if (!is_ok (error)) {
g_free (field);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ return NULL;
}
}
if (fb->offset != -1)
}
#endif
+/**
+ * mono_reflection_bind_generic_parameters:
+ * @type: a managed type object (which should be some kind of generic (instance? definition?))
+ * @type_args: the number of type arguments to bind
+ * @types: array of type arguments
+ * @error: set on error
+ *
+ * Given a managed type object for a generic type instance, binds each of its arguments to the specified types.
+ * Returns the MonoType* for the resulting type instantiation. On failure returns NULL and sets @error.
+ */
MonoType*
-mono_reflection_bind_generic_parameters (MonoReflectionType *type, int type_argc, MonoType **types)
+mono_reflection_bind_generic_parameters (MonoReflectionType *type, int type_argc, MonoType **types, MonoError *error)
{
- MonoError error;
MonoClass *klass;
MonoReflectionTypeBuilder *tb = NULL;
gboolean is_dynamic = FALSE;
MonoClass *geninst;
+ mono_error_init (error);
+
mono_loader_lock ();
if (is_sre_type_builder (mono_object_class (type))) {
if (tb && tb->generic_container)
mono_reflection_create_generic_class (tb);
- MonoType *t = mono_reflection_type_get_handle (type, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *t = mono_reflection_type_get_handle (type, error);
+ if (!is_ok (error)) {
+ mono_loader_unlock ();
+ return NULL;
+ }
klass = mono_class_from_mono_type (t);
if (!klass->generic_container) {
mono_loader_unlock ();
+ mono_error_set_type_load_class (error, klass, "Cannot bind generic parameters of a non-generic type");
return NULL;
}
}
/*TODO avoid saving custom attrs for generic classes as it's enough to have them on the generic type definition.*/
-void
-mono_reflection_generic_class_initialize (MonoReflectionGenericClass *type, MonoArray *fields)
+static gboolean
+reflection_generic_class_initialize (MonoReflectionGenericClass *type, MonoArray *fields, MonoError *error)
{
- MonoError error;
MonoGenericClass *gclass;
MonoDynamicGenericClass *dgclass;
MonoClass *klass, *gklass;
MonoType *gtype;
int i;
- gtype = mono_reflection_type_get_handle ((MonoReflectionType*)type, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_error_init (error);
+
+ gtype = mono_reflection_type_get_handle ((MonoReflectionType*)type, error);
+ return_val_if_nok (error, FALSE);
klass = mono_class_from_mono_type (gtype);
g_assert (gtype->type == MONO_TYPE_GENERICINST);
gclass = gtype->data.generic_class;
if (!gclass->is_dynamic)
- return;
+ return TRUE;
dgclass = (MonoDynamicGenericClass *) gclass;
if (dgclass->initialized)
- return;
+ return TRUE;
gklass = gclass->container_class;
mono_class_init (gklass);
dgclass->field_generic_types = mono_image_set_new0 (gclass->owner, MonoType*, dgclass->count_fields);
for (i = 0; i < dgclass->count_fields; i++) {
- MonoError error;
MonoObject *obj = (MonoObject *)mono_array_get (fields, gpointer, i);
MonoClassField *field, *inflated_field = NULL;
- if (!strcmp (obj->vtable->klass->name, "FieldBuilder"))
- inflated_field = field = fieldbuilder_to_mono_class_field (klass, (MonoReflectionFieldBuilder *) obj);
- else if (!strcmp (obj->vtable->klass->name, "MonoField"))
+ if (!strcmp (obj->vtable->klass->name, "FieldBuilder")) {
+ inflated_field = field = fieldbuilder_to_mono_class_field (klass, (MonoReflectionFieldBuilder *) obj, error);
+ return_val_if_nok (error, FALSE);
+ } else if (!strcmp (obj->vtable->klass->name, "MonoField"))
field = ((MonoReflectionField *) obj)->field;
else {
field = NULL; /* prevent compiler warning */
dgclass->fields [i] = *field;
dgclass->fields [i].parent = klass;
dgclass->fields [i].type = mono_class_inflate_generic_type_checked (
- field->type, mono_generic_class_get_context ((MonoGenericClass *) dgclass), &error);
- mono_error_assert_ok (&error); /* FIXME don't swallow the error */
+ field->type, mono_generic_class_get_context ((MonoGenericClass *) dgclass), error);
+ mono_error_assert_ok (error); /* FIXME don't swallow the error */
dgclass->field_generic_types [i] = field->type;
MONO_GC_REGISTER_ROOT_IF_MOVING (dgclass->field_objects [i], MONO_ROOT_SOURCE_REFLECTION, "dynamic generic class field object");
dgclass->field_objects [i] = obj;
}
dgclass->initialized = TRUE;
+ return TRUE;
+}
+
+void
+mono_reflection_generic_class_initialize (MonoReflectionGenericClass *type, MonoArray *fields)
+{
+ MonoError error;
+ (void) reflection_generic_class_initialize (type, fields, &error);
+ mono_error_set_pending_exception (&error);
}
void
}
}
-static void
-fix_partial_generic_class (MonoClass *klass)
+/**
+ * fix_partial_generic_class:
+ * @klass: a generic instantiation MonoClass
+ * @error: set on error
+ *
+ * Assumes that the generic container of @klass has its vtable
+ * initialized, and updates the parent class, insterfaces, methods and
+ * fields of @klass by inflating the types using the generic context.
+ *
+ * On success returns TRUE, on failure returns FALSE and sets @error.
+ *
+ */
+static gboolean
+fix_partial_generic_class (MonoClass *klass, MonoError *error)
{
MonoClass *gklass = klass->generic_class->container_class;
MonoDynamicGenericClass *dgclass;
int i;
+ mono_error_init (error);
+
if (klass->wastypebuilder)
- return;
+ return TRUE;
dgclass = (MonoDynamicGenericClass *) klass->generic_class;
if (klass->parent != gklass->parent) {
- MonoError error;
- MonoType *parent_type = mono_class_inflate_generic_type_checked (&gklass->parent->byval_arg, &klass->generic_class->context, &error);
- if (mono_error_ok (&error)) {
+ MonoType *parent_type = mono_class_inflate_generic_type_checked (&gklass->parent->byval_arg, &klass->generic_class->context, error);
+ if (mono_error_ok (error)) {
MonoClass *parent = mono_class_from_mono_type (parent_type);
mono_metadata_free_type (parent_type);
if (parent != klass->parent) {
mono_class_setup_parent (klass, parent);
}
} else {
- mono_class_set_failure (klass, MONO_EXCEPTION_TYPE_LOAD, NULL);
- mono_error_cleanup (&error);
if (gklass->wastypebuilder)
klass->wastypebuilder = TRUE;
- return;
+ return FALSE;
}
}
if (!dgclass->initialized)
- return;
+ return TRUE;
if (klass->method.count != gklass->method.count) {
klass->method.count = gklass->method.count;
klass->methods = (MonoMethod **)mono_image_alloc (klass->image, sizeof (MonoMethod*) * (klass->method.count + 1));
for (i = 0; i < klass->method.count; i++) {
- MonoError error;
klass->methods [i] = mono_class_inflate_generic_method_full_checked (
- gklass->methods [i], klass, mono_class_get_context (klass), &error);
- g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
+ gklass->methods [i], klass, mono_class_get_context (klass), error);
+ mono_error_assert_ok (error);
}
}
klass->interfaces_packed = NULL; /*make setup_interface_offsets happy*/
for (i = 0; i < gklass->interface_count; ++i) {
- MonoError error;
- MonoType *iface_type = mono_class_inflate_generic_type_checked (&gklass->interfaces [i]->byval_arg, mono_class_get_context (klass), &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *iface_type = mono_class_inflate_generic_type_checked (&gklass->interfaces [i]->byval_arg, mono_class_get_context (klass), error);
+ return_val_if_nok (error, FALSE);
klass->interfaces [i] = mono_class_from_mono_type (iface_type);
mono_metadata_free_type (iface_type);
- ensure_runtime_vtable (klass->interfaces [i], &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ if (!ensure_runtime_vtable (klass->interfaces [i], error))
+ return FALSE;
}
klass->interfaces_inited = 1;
}
klass->fields = image_g_new0 (klass->image, MonoClassField, klass->field.count);
for (i = 0; i < klass->field.count; i++) {
- MonoError error;
klass->fields [i] = gklass->fields [i];
klass->fields [i].parent = klass;
- klass->fields [i].type = mono_class_inflate_generic_type_checked (gklass->fields [i].type, mono_class_get_context (klass), &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ klass->fields [i].type = mono_class_inflate_generic_type_checked (gklass->fields [i].type, mono_class_get_context (klass), error);
+ return_val_if_nok (error, FALSE);
}
}
/*We can only finish with this klass once it's parent has as well*/
if (gklass->wastypebuilder)
klass->wastypebuilder = TRUE;
- return;
+ return TRUE;
}
/**
if (!ensure_runtime_vtable (gklass, error))
return FALSE;
- fix_partial_generic_class (klass);
-
- return TRUE;
+ return fix_partial_generic_class (klass, error);
}
/**
klass->interfaces_inited = 1;
}
} else if (klass->generic_class){
- if (!ensure_generic_class_runtime_vtable (klass, error))
+ if (!ensure_generic_class_runtime_vtable (klass, error)) {
+ mono_class_set_failure (klass, MONO_EXCEPTION_TYPE_LOAD, NULL);
return FALSE;
+ }
}
if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
}
static MonoMethod*
-mono_reflection_method_get_handle (MonoObject *method)
+mono_reflection_method_get_handle (MonoObject *method, MonoError *error)
{
- MonoError error;
+ mono_error_init (error);
MonoClass *klass = mono_object_class (method);
if (is_sr_mono_method (klass) || is_sr_mono_generic_method (klass)) {
MonoReflectionMethod *sr_method = (MonoReflectionMethod*)method;
MonoMethod *result;
/*FIXME move this to a proper method and unify with resolve_object*/
if (m->method_args) {
- result = mono_reflection_method_on_tb_inst_get_handle (m, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ result = mono_reflection_method_on_tb_inst_get_handle (m, error);
} else {
- MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)m->inst, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)m->inst, error);
+ return_val_if_nok (error, NULL);
MonoClass *inflated_klass = mono_class_from_mono_type (type);
MonoMethod *mono_method;
}
void
-mono_reflection_get_dynamic_overrides (MonoClass *klass, MonoMethod ***overrides, int *num_overrides)
+mono_reflection_get_dynamic_overrides (MonoClass *klass, MonoMethod ***overrides, int *num_overrides, MonoError *error)
{
MonoReflectionTypeBuilder *tb;
int i, j, onum;
MonoReflectionMethod *m;
+ mono_error_init (error);
*overrides = NULL;
*num_overrides = 0;
for (j = 0; j < mono_array_length (mb->override_methods); ++j) {
m = mono_array_get (mb->override_methods, MonoReflectionMethod*, j);
- (*overrides) [onum * 2] = mono_reflection_method_get_handle ((MonoObject*)m);
+ (*overrides) [onum * 2] = mono_reflection_method_get_handle ((MonoObject*)m, error);
+ return_if_nok (error);
(*overrides) [onum * 2 + 1] = mb->mhandle;
g_assert (mb->mhandle);
}
klass->instance_size = MAX (klass->instance_size, real_size);
- mono_class_layout_fields (klass);
+ mono_class_layout_fields (klass, klass->instance_size);
}
static void
}
}
-MonoReflectionEvent *
-mono_reflection_event_builder_get_event_info (MonoReflectionTypeBuilder *tb, MonoReflectionEventBuilder *eb)
+static MonoReflectionEvent *
+reflection_event_builder_get_event_info (MonoReflectionTypeBuilder *tb, MonoReflectionEventBuilder *eb, MonoError *error)
{
- MonoError error;
+ mono_error_init (error);
+
MonoEvent *event = g_new0 (MonoEvent, 1);
MonoClass *klass;
- MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)tb, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)tb, error);
+ if (!is_ok (error)) {
+ g_free (event);
+ return NULL;
+ }
klass = mono_class_from_mono_type (type);
event->parent = klass;
}
#endif
- MonoReflectionEvent *ev_obj = mono_event_get_object_checked (mono_object_domain (tb), klass, event, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoReflectionEvent *ev_obj = mono_event_get_object_checked (mono_object_domain (tb), klass, event, error);
+ if (!is_ok (error)) {
+#ifndef MONO_SMALL_CONFIG
+ g_free (event->other);
+#endif
+ g_free (event);
+ return NULL;
+ }
return ev_obj;
}
+MonoReflectionEvent *
+mono_reflection_event_builder_get_event_info (MonoReflectionTypeBuilder *tb, MonoReflectionEventBuilder *eb)
+{
+ MonoError error;
+ MonoReflectionEvent *result = reflection_event_builder_get_event_info (tb, eb, &error);
+ mono_error_set_pending_exception (&error);
+ return result;
+}
+
static void
typebuilder_setup_events (MonoClass *klass, MonoError *error)
{
}
}
+struct remove_instantiations_user_data
+{
+ MonoClass *klass;
+ MonoError *error;
+};
+
static gboolean
remove_instantiations_of_and_ensure_contents (gpointer key,
gpointer value,
gpointer user_data)
{
+ struct remove_instantiations_user_data *data = (struct remove_instantiations_user_data*)user_data;
MonoType *type = (MonoType*)key;
- MonoClass *klass = (MonoClass*)user_data;
+ MonoClass *klass = data->klass;
+ gboolean already_failed = !is_ok (data->error);
+ MonoError lerror;
+ MonoError *error = already_failed ? &lerror : data->error;
if ((type->type == MONO_TYPE_GENERICINST) && (type->data.generic_class->container_class == klass)) {
- fix_partial_generic_class (mono_class_from_mono_type (type)); //Ensure it's safe to use it.
+ MonoClass *inst_klass = mono_class_from_mono_type (type);
+ //Ensure it's safe to use it.
+ if (!fix_partial_generic_class (inst_klass, error)) {
+ mono_class_set_failure (inst_klass, MONO_EXCEPTION_TYPE_LOAD, NULL);
+ // Marked the class with failure, but since some other instantiation already failed,
+ // just report that one, and swallow the error from this one.
+ if (already_failed)
+ mono_error_cleanup (error);
+ }
return TRUE;
} else
return FALSE;
MonoReflectionType* res;
int i, j;
+ mono_error_init (&error);
+
domain = mono_object_domain (tb);
klass = mono_class_from_mono_type (tb->type.type);
* Check for user defined Type subclasses.
*/
RESOLVE_TYPE (tb->parent, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
check_array_for_usertypes (tb->interfaces, &error);
- mono_error_raise_exception (&error); /*FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
if (tb->fields) {
for (i = 0; i < mono_array_length (tb->fields); ++i) {
MonoReflectionFieldBuilder *fb = (MonoReflectionFieldBuilder *)mono_array_get (tb->fields, gpointer, i);
if (fb) {
RESOLVE_TYPE (fb->type, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
check_array_for_usertypes (fb->modreq, &error);
- mono_error_raise_exception (&error); /*FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
check_array_for_usertypes (fb->modopt, &error);
- mono_error_raise_exception (&error); /*FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
if (fb->marshal_info && fb->marshal_info->marshaltyperef) {
RESOLVE_TYPE (fb->marshal_info->marshaltyperef, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
}
}
}
MonoReflectionMethodBuilder *mb = (MonoReflectionMethodBuilder *)mono_array_get (tb->methods, gpointer, i);
if (mb) {
RESOLVE_TYPE (mb->rtype, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
check_array_for_usertypes (mb->return_modreq, &error);
- mono_error_raise_exception (&error); /*FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
check_array_for_usertypes (mb->return_modopt, &error);
- mono_error_raise_exception (&error); /*FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
check_array_for_usertypes (mb->parameters, &error);
- mono_error_raise_exception (&error); /*FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
if (mb->param_modreq)
for (j = 0; j < mono_array_length (mb->param_modreq); ++j) {
check_array_for_usertypes (mono_array_get (mb->param_modreq, MonoArray*, j), &error);
- mono_error_raise_exception (&error); /*FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
}
if (mb->param_modopt)
for (j = 0; j < mono_array_length (mb->param_modopt); ++j) {
check_array_for_usertypes (mono_array_get (mb->param_modopt, MonoArray*, j), &error);
- mono_error_raise_exception (&error); /*FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
}
}
}
MonoReflectionCtorBuilder *mb = (MonoReflectionCtorBuilder *)mono_array_get (tb->ctors, gpointer, i);
if (mb) {
check_array_for_usertypes (mb->parameters, &error);
- mono_error_raise_exception (&error); /*FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
if (mb->param_modreq)
for (j = 0; j < mono_array_length (mb->param_modreq); ++j) {
check_array_for_usertypes (mono_array_get (mb->param_modreq, MonoArray*, j), &error);
- mono_error_raise_exception (&error); /*FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
}
if (mb->param_modopt)
for (j = 0; j < mono_array_length (mb->param_modopt); ++j) {
check_array_for_usertypes (mono_array_get (mb->param_modopt, MonoArray*, j), &error);
- mono_error_raise_exception (&error); /*FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
}
}
}
mono_loader_unlock ();
res = mono_type_get_object_checked (mono_object_domain (tb), &klass->byval_arg, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_error_set_pending_exception (&error);
return res;
}
mono_domain_unlock (domain);
res = mono_type_get_object_checked (mono_object_domain (tb), &klass->byval_arg, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_error_set_pending_exception (&error);
return res;
}
*
* Together with this we must ensure the contents of all instances to match the created type.
*/
- if (domain->type_hash && klass->generic_container)
- mono_g_hash_table_foreach_remove (domain->type_hash, remove_instantiations_of_and_ensure_contents, klass);
+ if (domain->type_hash && klass->generic_container) {
+ struct remove_instantiations_user_data data;
+ data.klass = klass;
+ data.error = &error;
+ mono_error_assert_ok (&error);
+ mono_g_hash_table_foreach_remove (domain->type_hash, remove_instantiations_of_and_ensure_contents, &data);
+ if (!is_ok (&error))
+ goto failure;
+ }
mono_domain_unlock (domain);
mono_loader_unlock ();
if (klass->enumtype && !mono_class_is_valid_enum (klass)) {
mono_class_set_failure (klass, MONO_EXCEPTION_TYPE_LOAD, NULL);
- mono_raise_exception (mono_get_exception_type_load (tb->name, NULL));
+ mono_error_set_type_load_class (&error, klass, "Not a valid enumeration");
+ goto failure_unlocked;
}
res = mono_type_get_object_checked (mono_object_domain (tb), &klass->byval_arg, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ if (!is_ok (&error))
+ goto failure_unlocked;
g_assert (res != (MonoReflectionType*)tb);
klass->wastypebuilder = TRUE;
mono_domain_unlock (domain);
mono_loader_unlock ();
- mono_error_raise_exception (&error);
+failure_unlocked:
+ mono_error_set_pending_exception (&error);
return NULL;
}
-void
-mono_reflection_initialize_generic_parameter (MonoReflectionGenericParam *gparam)
+static gboolean
+reflection_initialize_generic_parameter (MonoReflectionGenericParam *gparam, MonoError *error)
{
MonoGenericParamFull *param;
MonoImage *image;
MonoClass *pklass;
- MonoError error;
+
+ mono_error_init (error);
image = &gparam->tbuilder->module->dynamic_image->image;
param = mono_image_new0 (image, MonoGenericParamFull, 1);
- param->info.name = mono_string_to_utf8_image (image, gparam->name, &error);
- g_assert (mono_error_ok (&error));
+ param->info.name = mono_string_to_utf8_image (image, gparam->name, error);
+ mono_error_assert_ok (error);
param->param.num = gparam->index;
if (gparam->mbuilder) {
if (!gparam->mbuilder->generic_container) {
- MonoType *tb = mono_reflection_type_get_handle ((MonoReflectionType*)gparam->mbuilder->type, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *tb = mono_reflection_type_get_handle ((MonoReflectionType*)gparam->mbuilder->type, error);
+ return_val_if_nok (error, FALSE);
MonoClass *klass = mono_class_from_mono_type (tb);
gparam->mbuilder->generic_container = (MonoGenericContainer *)mono_image_alloc0 (klass->image, sizeof (MonoGenericContainer));
param->param.owner = gparam->mbuilder->generic_container;
} else if (gparam->tbuilder) {
if (!gparam->tbuilder->generic_container) {
- MonoType *tb = mono_reflection_type_get_handle ((MonoReflectionType*)gparam->tbuilder, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *tb = mono_reflection_type_get_handle ((MonoReflectionType*)gparam->tbuilder, error);
+ return_val_if_nok (error, FALSE);
MonoClass *klass = mono_class_from_mono_type (tb);
gparam->tbuilder->generic_container = (MonoGenericContainer *)mono_image_alloc0 (klass->image, sizeof (MonoGenericContainer));
gparam->tbuilder->generic_container->owner.klass = klass;
mono_class_set_ref_info (pklass, gparam);
mono_image_append_class_to_reflection_info_set (pklass);
+
+ return TRUE;
+}
+
+void
+mono_reflection_initialize_generic_parameter (MonoReflectionGenericParam *gparam)
+{
+ MonoError error;
+ (void) reflection_initialize_generic_parameter (gparam, &error);
+ mono_error_set_pending_exception (&error);
}
+
MonoArray *
mono_reflection_sighelper_get_signature_local (MonoReflectionSigHelper *sig)
{
g_free (data);
}
-void
-mono_reflection_create_dynamic_method (MonoReflectionDynamicMethod *mb)
+static gboolean
+reflection_create_dynamic_method (MonoReflectionDynamicMethod *mb, MonoError *error)
{
- MonoError error;
MonoReferenceQueue *queue;
MonoMethod *handle;
DynamicMethodReleaseData *release_data;
GSList *l;
int i;
- if (mono_runtime_is_shutting_down ())
- mono_raise_exception (mono_get_exception_invalid_operation (""));
+ mono_error_init (error);
+
+ if (mono_runtime_is_shutting_down ()) {
+ mono_error_set_generic_error (error, "System", "InvalidOperationException", "");
+ return FALSE;
+ }
if (!(queue = dynamic_method_queue)) {
mono_loader_lock ();
mono_loader_unlock ();
}
- sig = dynamic_method_to_signature (mb);
+ sig = dynamic_method_to_signature (mb, error);
+ return_val_if_nok (error, FALSE);
reflection_methodbuilder_from_dynamic_method (&rmb, mb);
handle_class = mono_defaults.methodhandle_class;
} else {
MonoException *ex = NULL;
- ref = resolve_object (mb->module->image, obj, &handle_class, NULL);
+ ref = resolve_object (mb->module->image, obj, &handle_class, NULL, error);
+ if (!is_ok (error)) {
+ g_free (rmb.refs);
+ return FALSE;
+ }
if (!ref)
ex = mono_get_exception_type_load (NULL, NULL);
else if (mono_security_core_clr_enabled ())
if (ex) {
g_free (rmb.refs);
- mono_raise_exception (ex);
- return;
+ mono_error_set_exception_instance (error, ex);
+ return FALSE;
}
}
}
if (mb->owner) {
- MonoType *owner_type = mono_reflection_type_get_handle ((MonoReflectionType*)mb->owner, &error);
- if (!is_ok (&error)) {
+ MonoType *owner_type = mono_reflection_type_get_handle ((MonoReflectionType*)mb->owner, error);
+ if (!is_ok (error)) {
g_free (rmb.refs);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ return FALSE;
}
klass = mono_class_from_mono_type (owner_type);
} else {
domain->method_to_dyn_method = g_hash_table_new (NULL, NULL);
g_hash_table_insert (domain->method_to_dyn_method, handle, (gpointer)(size_t)mono_gchandle_new_weakref ((MonoObject *)mb, TRUE));
mono_domain_unlock (domain);
+
+ return TRUE;
+}
+
+void
+mono_reflection_create_dynamic_method (MonoReflectionDynamicMethod *mb)
+{
+ MonoError error;
+ (void) reflection_create_dynamic_method (mb, &error);
+ mono_error_set_pending_exception (&error);
}
#endif /* DISABLE_REFLECTION_EMIT */
* LOCKING: Take the loader lock
*/
gpointer
-mono_reflection_lookup_dynamic_token (MonoImage *image, guint32 token, gboolean valid_token, MonoClass **handle_class, MonoGenericContext *context)
+mono_reflection_lookup_dynamic_token (MonoImage *image, guint32 token, gboolean valid_token, MonoClass **handle_class, MonoGenericContext *context, MonoError *error)
{
MonoDynamicImage *assembly = (MonoDynamicImage*)image;
MonoObject *obj;
MonoClass *klass;
+ mono_error_init (error);
+
obj = lookup_dyn_token (assembly, token);
if (!obj) {
if (valid_token)
g_error ("Could not find required dynamic token 0x%08x", token);
- else
+ else {
+ mono_error_set_execution_engine (error, "Could not find dynamic token 0x%08x", token);
return NULL;
+ }
}
if (!handle_class)
handle_class = &klass;
- return resolve_object (image, obj, handle_class, context);
+ gpointer result = resolve_object (image, obj, handle_class, context, error);
+ return result;
}
/*
* dynamic types.
*/
static void
-ensure_complete_type (MonoClass *klass)
+ensure_complete_type (MonoClass *klass, MonoError *error)
{
- MonoError error;
+ mono_error_init (error);
if (image_is_dynamic (klass->image) && !klass->wastypebuilder && mono_class_get_ref_info (klass)) {
MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mono_class_get_ref_info (klass);
- mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, error);
+ return_if_nok (error);
// Asserting here could break a lot of code
//g_assert (klass->wastypebuilder);
int i;
for (i = 0; i < inst->type_argc; ++i) {
- ensure_complete_type (mono_class_from_mono_type (inst->type_argv [i]));
+ ensure_complete_type (mono_class_from_mono_type (inst->type_argv [i]), error);
+ return_if_nok (error);
}
}
}
static gpointer
-resolve_object (MonoImage *image, MonoObject *obj, MonoClass **handle_class, MonoGenericContext *context)
+resolve_object (MonoImage *image, MonoObject *obj, MonoClass **handle_class, MonoGenericContext *context, MonoError *error)
{
- MonoError error;
gpointer result = NULL;
+ mono_error_init (error);
+
if (strcmp (obj->vtable->klass->name, "String") == 0) {
- result = mono_string_intern_checked ((MonoString*)obj, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ result = mono_string_intern_checked ((MonoString*)obj, error);
+ return_val_if_nok (error, NULL);
*handle_class = mono_defaults.string_class;
g_assert (result);
} else if (strcmp (obj->vtable->klass->name, "MonoType") == 0) {
- MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)obj, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)obj, error);
+ return_val_if_nok (error, NULL);
MonoClass *mc = mono_class_from_mono_type (type);
- if (!mono_class_init (mc))
- mono_raise_exception (mono_class_get_exception_for_failure (mc));
+ if (!mono_class_init (mc)) {
+ mono_error_set_exception_instance (error, mono_class_get_exception_for_failure (mc));
+ return NULL;
+ }
if (context) {
- MonoType *inflated = mono_class_inflate_generic_type_checked (type, context, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *inflated = mono_class_inflate_generic_type_checked (type, context, error);
+ return_val_if_nok (error, NULL);
result = mono_class_from_mono_type (inflated);
mono_metadata_free_type (inflated);
strcmp (obj->vtable->klass->name, "MonoGenericMethod") == 0) {
result = ((MonoReflectionMethod*)obj)->method;
if (context) {
- MonoError error;
- result = mono_class_inflate_generic_method_checked ((MonoMethod *)result, context, &error);
- g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
+ result = mono_class_inflate_generic_method_checked ((MonoMethod *)result, context, error);
+ mono_error_assert_ok (error);
}
*handle_class = mono_defaults.methodhandle_class;
g_assert (result);
/* Type is not yet created */
MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder*)mb->type;
- mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, error);
+ return_val_if_nok (error, NULL);
/*
* Hopefully this has been filled in by calling CreateType() on the
result = mb->mhandle;
}
if (context) {
- MonoError error;
- result = mono_class_inflate_generic_method_checked ((MonoMethod *)result, context, &error);
- g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
+ result = mono_class_inflate_generic_method_checked ((MonoMethod *)result, context, error);
+ mono_error_assert_ok (error);
}
*handle_class = mono_defaults.methodhandle_class;
} else if (strcmp (obj->vtable->klass->name, "ConstructorBuilder") == 0) {
if (!result) {
MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder*)cb->type;
- mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, error);
+ return_val_if_nok (error, NULL);
result = cb->mhandle;
}
if (context) {
- MonoError error;
- result = mono_class_inflate_generic_method_checked ((MonoMethod *)result, context, &error);
- g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
+ result = mono_class_inflate_generic_method_checked ((MonoMethod *)result, context, error);
+ mono_error_assert_ok (error);
}
*handle_class = mono_defaults.methodhandle_class;
} else if (strcmp (obj->vtable->klass->name, "MonoField") == 0) {
MonoClassField *field = ((MonoReflectionField*)obj)->field;
- ensure_complete_type (field->parent);
+ ensure_complete_type (field->parent, error);
+ return_val_if_nok (error, NULL);
+
if (context) {
- MonoType *inflated = mono_class_inflate_generic_type_checked (&field->parent->byval_arg, context, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *inflated = mono_class_inflate_generic_type_checked (&field->parent->byval_arg, context, error);
+ return_val_if_nok (error, NULL);
MonoClass *klass = mono_class_from_mono_type (inflated);
MonoClassField *inflated_field;
if (!result) {
MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder*)fb->typeb;
- mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, error);
+ return_val_if_nok (error, NULL);
result = fb->handle;
}
if (fb->handle && fb->handle->parent->generic_container) {
MonoClass *klass = fb->handle->parent;
- MonoType *type = mono_class_inflate_generic_type_checked (&klass->byval_arg, context, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *type = mono_class_inflate_generic_type_checked (&klass->byval_arg, context, error);
+ return_val_if_nok (error, NULL);
MonoClass *inflated = mono_class_from_mono_type (type);
*handle_class = mono_defaults.fieldhandle_class;
} else if (strcmp (obj->vtable->klass->name, "TypeBuilder") == 0) {
MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder*)obj;
- MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)tb, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)tb, error);
+ return_val_if_nok (error, NULL);
MonoClass *klass;
klass = type->data.klass;
result = klass;
}
else {
- mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, error);
+ return_val_if_nok (error, NULL);
result = type->data.klass;
g_assert (result);
}
/* TODO: Copy type ? */
sig->ret = helper->return_type->type;
for (i = 0; i < nargs; ++i) {
- sig->params [i] = mono_type_array_get_and_resolve (helper->arguments, i, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ sig->params [i] = mono_type_array_get_and_resolve (helper->arguments, i, error);
+ if (!is_ok (error)) {
+ image_g_free (image, sig);
+ return NULL;
+ }
}
result = sig;
result = method->mhandle;
*handle_class = mono_defaults.methodhandle_class;
} else if (strcmp (obj->vtable->klass->name, "GenericTypeParameterBuilder") == 0) {
- MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)obj, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
- type = mono_class_inflate_generic_type_checked (type, context, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)obj, error);
+ return_val_if_nok (error, NULL);
+ type = mono_class_inflate_generic_type_checked (type, context, error);
+ return_val_if_nok (error, NULL);
result = mono_class_from_mono_type (type);
*handle_class = mono_defaults.typehandle_class;
g_assert (result);
mono_metadata_free_type (type);
} else if (strcmp (obj->vtable->klass->name, "MonoGenericClass") == 0) {
- MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)obj, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
- type = mono_class_inflate_generic_type_checked (type, context, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)obj, error);
+ return_val_if_nok (error, NULL);
+ type = mono_class_inflate_generic_type_checked (type, context, error);
+ return_val_if_nok (error, NULL);
result = mono_class_from_mono_type (type);
*handle_class = mono_defaults.typehandle_class;
else
g_error ("resolve_object:: can't handle a FTBI with base_method of type %s", mono_type_get_full_name (mono_object_class (f->fb)));
- MonoType *finst = mono_reflection_type_get_handle ((MonoReflectionType*)f->inst, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
- type = mono_class_inflate_generic_type_checked (finst, context, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *finst = mono_reflection_type_get_handle ((MonoReflectionType*)f->inst, error);
+ return_val_if_nok (error, NULL);
+ type = mono_class_inflate_generic_type_checked (finst, context, error);
+ return_val_if_nok (error, NULL);
inflated = mono_class_from_mono_type (type);
result = field = mono_class_get_field_from_name (inflated, mono_field_get_name (field));
- ensure_complete_type (field->parent);
+ ensure_complete_type (field->parent, error);
+ if (!is_ok (error)) {
+ mono_metadata_free_type (type);
+ return NULL;
+ }
+
g_assert (result);
mono_metadata_free_type (type);
*handle_class = mono_defaults.fieldhandle_class;
} else if (strcmp (obj->vtable->klass->name, "ConstructorOnTypeBuilderInst") == 0) {
MonoReflectionCtorOnTypeBuilderInst *c = (MonoReflectionCtorOnTypeBuilderInst*)obj;
- MonoType *cinst = mono_reflection_type_get_handle ((MonoReflectionType*)c->inst, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
- MonoType *type = mono_class_inflate_generic_type_checked (cinst, context, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *cinst = mono_reflection_type_get_handle ((MonoReflectionType*)c->inst, error);
+ return_val_if_nok (error, NULL);
+ MonoType *type = mono_class_inflate_generic_type_checked (cinst, context, error);
+ return_val_if_nok (error, NULL);
MonoClass *inflated_klass = mono_class_from_mono_type (type);
MonoMethod *method;
} else if (strcmp (obj->vtable->klass->name, "MethodOnTypeBuilderInst") == 0) {
MonoReflectionMethodOnTypeBuilderInst *m = (MonoReflectionMethodOnTypeBuilderInst*)obj;
if (m->method_args) {
- result = mono_reflection_method_on_tb_inst_get_handle (m, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ result = mono_reflection_method_on_tb_inst_get_handle (m, error);
+ return_val_if_nok (error, NULL);
if (context) {
- result = mono_class_inflate_generic_method_checked ((MonoMethod *)result, context, &error);
- mono_error_assert_ok (&error);
+ result = mono_class_inflate_generic_method_checked ((MonoMethod *)result, context, error);
+ mono_error_assert_ok (error);
}
} else {
- MonoType *minst = mono_reflection_type_get_handle ((MonoReflectionType*)m->inst, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
- MonoType *type = mono_class_inflate_generic_type_checked (minst, context, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *minst = mono_reflection_type_get_handle ((MonoReflectionType*)m->inst, error);
+ return_val_if_nok (error, NULL);
+ MonoType *type = mono_class_inflate_generic_type_checked (minst, context, error);
+ return_val_if_nok (error, NULL);
MonoClass *inflated_klass = mono_class_from_mono_type (type);
MonoMethod *method;
gpointer iter;
char *name;
- mtype = mono_reflection_type_get_handle (m->parent, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mtype = mono_reflection_type_get_handle (m->parent, error);
+ return_val_if_nok (error, NULL);
klass = mono_class_from_mono_type (mtype);
/* Find the method */
is_sre_byref (mono_object_get_class(obj)) ||
is_sre_pointer (mono_object_get_class(obj))) {
MonoReflectionType *ref_type = (MonoReflectionType *)obj;
- MonoType *type = mono_reflection_type_get_handle (ref_type, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *type = mono_reflection_type_get_handle (ref_type, error);
+ return_val_if_nok (error, NULL);
if (context) {
- MonoType *inflated = mono_class_inflate_generic_type_checked (type, context, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoType *inflated = mono_class_inflate_generic_type_checked (type, context, error);
+ return_val_if_nok (error, NULL);
result = mono_class_from_mono_type (inflated);
mono_metadata_free_type (inflated);
}
void
-mono_reflection_get_dynamic_overrides (MonoClass *klass, MonoMethod ***overrides, int *num_overrides)
+mono_reflection_get_dynamic_overrides (MonoClass *klass, MonoMethod ***overrides, int *num_overrides, MonoError *error)
{
+ mono_error_init (error);
*overrides = NULL;
*num_overrides = 0;
}
}
gboolean
-mono_reflection_call_is_assignable_to (MonoClass *klass, MonoClass *oklass)
+mono_reflection_call_is_assignable_to (MonoClass *klass, MonoClass *oklass, MonoError *error)
{
- MonoError error;
MonoObject *res, *exc;
void *params [1];
static MonoMethod *method = NULL;
+ mono_error_init (error);
+
if (method == NULL) {
method = mono_class_get_method_from_name (mono_class_get_type_builder_class (), "IsAssignableTo", 1);
g_assert (method);
g_assert (mono_class_get_ref_info (klass));
g_assert (!strcmp (((MonoObject*)(mono_class_get_ref_info (klass)))->vtable->klass->name, "TypeBuilder"));
- params [0] = mono_type_get_object_checked (mono_domain_get (), &oklass->byval_arg, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ params [0] = mono_type_get_object_checked (mono_domain_get (), &oklass->byval_arg, error);
+ return_val_if_nok (error, FALSE);
- res = mono_runtime_try_invoke (method, (MonoObject*)(mono_class_get_ref_info (klass)), params, &exc, &error);
+ res = mono_runtime_try_invoke (method, (MonoObject*)(mono_class_get_ref_info (klass)), params, &exc, error);
- if (exc || !mono_error_ok (&error)) {
- mono_error_cleanup (&error);
+ if (exc || !mono_error_ok (error)) {
+ mono_error_cleanup (error);
return FALSE;
} else
return *(MonoBoolean*)mono_object_unbox (res);
MONO_API MonoArray* mono_reflection_get_custom_attrs (MonoObject *obj);
MONO_RT_EXTERNAL_ONLY
MONO_API MonoArray* mono_reflection_get_custom_attrs_data (MonoObject *obj);
+MONO_RT_EXTERNAL_ONLY
MONO_API MonoArray* mono_reflection_get_custom_attrs_blob (MonoReflectionAssembly *assembly, MonoObject *ctor, MonoArray *ctorArgs, MonoArray *properties, MonoArray *porpValues, MonoArray *fields, MonoArray* fieldValues);
MONO_RT_EXTERNAL_ONLY
mparams[i] = *((gpointer *)params [i]);
} else {
/* runtime_invoke expects a boxed instance */
- if (mono_class_is_nullable (mono_class_from_mono_type (sig->params [i])))
- mparams[i] = mono_nullable_box ((guint8 *)params [i], klass);
- else
+ if (mono_class_is_nullable (mono_class_from_mono_type (sig->params [i]))) {
+ mparams[i] = mono_nullable_box ((guint8 *)params [i], klass, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ } else
mparams[i] = params [i];
}
} else {
case MONO_TYPE_U8:
case MONO_TYPE_R4:
case MONO_TYPE_R8: {
- return mono_value_box (domain, mono_object_class (val), ((char*)val) + sizeof(MonoObject));
+ MonoObject *res = mono_value_box_checked (domain, mono_object_class (val), ((char*)val) + sizeof(MonoObject), &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ return res;
+
}
case MONO_TYPE_STRING: {
MonoString *str = (MonoString *) val;
bridge_callbacks = *callbacks;
if (!bridge_processor.reset_data)
- sgen_old_bridge_init (&bridge_processor);
+ sgen_new_bridge_init (&bridge_processor);
}
static gboolean
#define SGEN_TV_DECLARE(name) gint64 name
#define SGEN_TV_GETTIME(tv) tv = mono_100ns_ticks ()
-#define SGEN_TV_ELAPSED(start,end) ((long)(end-start))
+#define SGEN_TV_ELAPSED(start,end) ((gint64)(end-start))
typedef MonoSemType SgenSemaphore;
#define ARRAY_OBJ_INDEX(ptr,array,elem_size) (((char*)(ptr) - ((char*)(array) + G_STRUCT_OFFSET (MonoArray, vector))) / (elem_size))
gboolean
-sgen_client_cardtable_scan_object (GCObject *obj, mword block_obj_size, guint8 *cards, gboolean mod_union, ScanCopyContext ctx)
+sgen_client_cardtable_scan_object (GCObject *obj, mword block_obj_size, guint8 *cards, ScanCopyContext ctx)
{
MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
MonoClass *klass = vt->klass;
for (; elem < card_end; elem += elem_size)
scan_vtype_func (obj, elem, desc, ctx.queue BINARY_PROTOCOL_ARG (elem_size));
} else {
- CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
+ ScanPtrFieldFunc scan_ptr_field_func = ctx.ops->scan_ptr_field;
HEAVY_STAT (++los_array_cards);
- for (; elem < card_end; elem += SIZEOF_VOID_P) {
- GCObject *new_;
- gpointer old = *(gpointer*)elem;
- if ((mod_union && old) || G_UNLIKELY (sgen_ptr_in_nursery (old))) {
- HEAVY_STAT (++los_array_remsets);
- copy_func ((GCObject**)elem, ctx.queue);
- new_ = *(GCObject **)elem;
- if (G_UNLIKELY (sgen_ptr_in_nursery (new_)))
- sgen_add_to_global_remset (elem, new_);
- }
- }
+ for (; elem < card_end; elem += SIZEOF_VOID_P)
+ scan_ptr_field_func (obj, (GCObject**)elem, ctx.queue);
}
binary_protocol_card_scan (first_elem, elem - first_elem);
DEF_QSORT_INLINE(hash_entries, HashEntry*, compare_hash_entries)
-static unsigned long step_1, step_2, step_3, step_4, step_5, step_6;
+static gint64 step_1, step_2, step_3, step_4, step_5, step_6;
static int fist_pass_links, second_pass_links, sccs_links;
static int max_sccs_links = 0;
DEF_QSORT_INLINE(hash_entries, HashEntry*, compare_hash_entries)
-static unsigned long step_1, step_2, step_3, step_4, step_5, step_6;
+static gint64 step_1, step_2, step_3, step_4, step_5, step_6;
static int fist_pass_links, second_pass_links, sccs_links;
static int max_sccs_links = 0;
static MonoObject*
int_to_object (MonoDomain *domain, int val)
{
- return mono_value_box (domain, mono_get_int32_class (), &val);
+ MonoError error;
+ MonoObject *result = mono_value_box_checked (domain, mono_get_int32_class (), &val, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ return result;
}
void
static void
wait_callback (gint fd, gint events, gpointer user_data)
{
+ MonoError error;
+
if (mono_runtime_is_shutting_down ())
return;
if (list && (events & EVENT_IN) != 0) {
MonoIOSelectorJob *job = get_job_for_event (&list, EVENT_IN);
- if (job)
- mono_threadpool_ms_enqueue_work_item (((MonoObject*) job)->vtable->domain, (MonoObject*) job);
+ if (job) {
+ mono_threadpool_ms_enqueue_work_item (((MonoObject*) job)->vtable->domain, (MonoObject*) job, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ }
+
}
if (list && (events & EVENT_OUT) != 0) {
MonoIOSelectorJob *job = get_job_for_event (&list, EVENT_OUT);
- if (job)
- mono_threadpool_ms_enqueue_work_item (((MonoObject*) job)->vtable->domain, (MonoObject*) job);
+ if (job) {
+ mono_threadpool_ms_enqueue_work_item (((MonoObject*) job)->vtable->domain, (MonoObject*) job, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ }
}
remove_fd = (events & EVENT_ERR) == EVENT_ERR;
static void
selector_thread (gpointer data)
{
+ MonoError error;
MonoGHashTable *states;
io_selector_running = TRUE;
memset (update, 0, sizeof (ThreadPoolIOUpdate));
}
- for (; list; list = mono_mlist_remove_item (list, list))
- mono_threadpool_ms_enqueue_work_item (mono_object_domain (mono_mlist_get_data (list)), mono_mlist_get_data (list));
+ for (; list; list = mono_mlist_remove_item (list, list)) {
+ mono_threadpool_ms_enqueue_work_item (mono_object_domain (mono_mlist_get_data (list)), mono_mlist_get_data (list), &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ }
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: del fd %3d", fd);
threadpool_io->backend.remove_fd (fd);
mono_coop_mutex_unlock (&threadpool->active_threads_lock);
}
-void
-mono_threadpool_ms_enqueue_work_item (MonoDomain *domain, MonoObject *work_item)
+gboolean
+mono_threadpool_ms_enqueue_work_item (MonoDomain *domain, MonoObject *work_item, MonoError *error)
{
static MonoClass *threadpool_class = NULL;
static MonoMethod *unsafe_queue_custom_work_item_method = NULL;
- MonoError error;
MonoDomain *current_domain;
MonoBoolean f;
gpointer args [2];
+ mono_error_init (error);
g_assert (work_item);
if (!threadpool_class)
current_domain = mono_domain_get ();
if (current_domain == domain) {
- mono_runtime_invoke_checked (unsafe_queue_custom_work_item_method, NULL, args, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_runtime_invoke_checked (unsafe_queue_custom_work_item_method, NULL, args, error);
+ return_val_if_nok (error, FALSE);
} else {
mono_thread_push_appdomain_ref (domain);
if (mono_domain_set (domain, FALSE)) {
- mono_runtime_invoke_checked (unsafe_queue_custom_work_item_method, NULL, args, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ mono_runtime_invoke_checked (unsafe_queue_custom_work_item_method, NULL, args, error);
+ if (!is_ok (error)) {
+ mono_thread_pop_appdomain_ref ();
+ return FALSE;
+ }
mono_domain_set (current_domain, TRUE);
}
mono_thread_pop_appdomain_ref ();
}
+ return TRUE;
}
/* LOCKING: threadpool->domains_lock must be held */
}
MonoAsyncResult *
-mono_threadpool_ms_begin_invoke (MonoDomain *domain, MonoObject *target, MonoMethod *method, gpointer *params)
+mono_threadpool_ms_begin_invoke (MonoDomain *domain, MonoObject *target, MonoMethod *method, gpointer *params, MonoError *error)
{
static MonoClass *async_call_klass = NULL;
- MonoError error;
MonoMethodMessage *message;
MonoAsyncResult *async_result;
MonoAsyncCall *async_call;
mono_lazy_initialize (&status, initialize);
+ mono_error_init (error);
+
message = mono_method_call_message_new (method, params, mono_get_delegate_invoke (method->klass), (params != NULL) ? (&async_callback) : NULL, (params != NULL) ? (&state) : NULL);
- async_call = (MonoAsyncCall*) mono_object_new_checked (domain, async_call_klass, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ async_call = (MonoAsyncCall*) mono_object_new_checked (domain, async_call_klass, error);
+ return_val_if_nok (error, NULL);
MONO_OBJECT_SETREF (async_call, msg, message);
MONO_OBJECT_SETREF (async_call, state, state);
async_result = mono_async_result_new (domain, NULL, async_call->state, NULL, (MonoObject*) async_call);
MONO_OBJECT_SETREF (async_result, async_delegate, target);
- mono_threadpool_ms_enqueue_work_item (domain, (MonoObject*) async_result);
+ mono_threadpool_ms_enqueue_work_item (domain, (MonoObject*) async_result, error);
+ return_val_if_nok (error, NULL);
return async_result;
}
ves_icall_System_Threading_ThreadPool_ReportThreadStatus (MonoBoolean is_working)
{
// TODO
- mono_raise_exception (mono_get_exception_not_implemented (NULL));
+ MonoError error;
+ mono_error_set_not_implemented (&error, "");
+ mono_error_set_pending_exception (&error);
}
MonoBoolean
ves_icall_System_Threading_ThreadPool_PostQueuedCompletionStatus (MonoNativeOverlapped *native_overlapped)
{
/* This copy the behavior of the current Mono implementation */
- mono_raise_exception (mono_get_exception_not_implemented (NULL));
+ MonoError error;
+ mono_error_set_not_implemented (&error, "");
+ mono_error_set_pending_exception (&error);
return FALSE;
}
mono_threadpool_ms_cleanup (void);
MonoAsyncResult *
-mono_threadpool_ms_begin_invoke (MonoDomain *domain, MonoObject *target, MonoMethod *method, gpointer *params);
+mono_threadpool_ms_begin_invoke (MonoDomain *domain, MonoObject *target, MonoMethod *method, gpointer *params, MonoError *error);
MonoObject *
mono_threadpool_ms_end_invoke (MonoAsyncResult *ares, MonoArray **out_args, MonoObject **exc);
/* Internals */
-void
-mono_threadpool_ms_enqueue_work_item (MonoDomain *domain, MonoObject *work_item);
+gboolean
+mono_threadpool_ms_enqueue_work_item (MonoDomain *domain, MonoObject *work_item, MonoError *error);
#endif // _MONO_THREADPOOL_MICROSOFT_H_
GPtrArray *patches;
MonoJumpInfo *patch_info;
MonoDebugSourceLocation **locs = NULL;
- gboolean skip;
+ gboolean skip, prologue_end = FALSE;
#ifdef MONO_ARCH_AOT_SUPPORTED
gboolean direct_call, external_call;
guint32 got_slot;
if (locs && locs [i]) {
MonoDebugSourceLocation *loc = locs [i];
int findex;
+ const char *options;
findex = get_file_index (acfg, loc->source_file);
emit_unset_mode (acfg);
- fprintf (acfg->fp, ".loc %d %d 0\n", findex, loc->row);
+ if (!prologue_end)
+ options = " prologue_end";
+ else
+ options = "";
+ prologue_end = TRUE;
+ fprintf (acfg->fp, ".loc %d %d 0%s\n", findex, loc->row, options);
mono_debug_symfile_free_location (loc);
}
}
}
- if (acfg->aot_opts.dwarf_debug && acfg->aot_opts.asm_only && acfg->aot_opts.gnu_asm) {
+ if (acfg->aot_opts.dwarf_debug && acfg->aot_opts.gnu_asm) {
/*
* CLANG supports GAS .file/.loc directives, so emit line number information this way
- * FIXME: CLANG only emits line number info for .loc directives followed by assembly, not
- * .byte directives.
*/
- //acfg->gas_line_numbers = TRUE;
+ acfg->gas_line_numbers = TRUE;
}
if ((!acfg->aot_opts.nodebug || acfg->aot_opts.dwarf_debug) && acfg->has_jitted_code) {
aot_printerrf (acfg, "The dwarf AOT option requires the --debug option.\n");
return 1;
}
- acfg->dwarf = mono_dwarf_writer_create (acfg->w, NULL, 0, FALSE, !acfg->gas_line_numbers);
+ acfg->dwarf = mono_dwarf_writer_create (acfg->w, NULL, 0, !acfg->gas_line_numbers);
}
if (acfg->w)
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT module '%s' not found: %s\n", aot_name, err);
g_free (err);
- aot_name = g_strdup_printf ("%s/mono/aot-cache/%s/%s%s", mono_assembly_getrootdir(), MONO_ARCHITECTURE, g_path_get_basename (assembly->image->name), MONO_SOLIB_EXT);
+ g_free (aot_name);
+ char *basename = g_path_get_basename (assembly->image->name);
+ aot_name = g_strdup_printf ("%s/mono/aot-cache/%s/%s%s", mono_assembly_getrootdir(), MONO_ARCHITECTURE, basename, MONO_SOLIB_EXT);
+ g_free (basename);
sofile = mono_dl_open (aot_name, MONO_DL_LAZY, &err);
if (!sofile) {
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT module '%s' not found: %s\n", aot_name, err);
mono_error_cleanup (&error); /* FIXME don't swallow the error */
}
- gi->generic_sharing_context = g_new0 (MonoGenericSharingContext, 1);
+ gi->generic_sharing_context = alloc0_jit_info_data (domain, sizeof (MonoGenericSharingContext), async);
if (decode_value (p, &p)) {
/* gsharedvt */
MonoGenericSharingContext *gsctx = gi->generic_sharing_context;
} else if (type == VALUE_TYPE_ID_NULL) {
*(MonoObject**)addr = NULL;
} else if (type == MONO_TYPE_VALUETYPE) {
+ MonoError error;
guint8 *buf2;
gboolean is_enum;
MonoClass *klass;
g_free (vtype_buf);
return err;
}
- *(MonoObject**)addr = mono_value_box (d, klass, vtype_buf);
+ *(MonoObject**)addr = mono_value_box_checked (d, klass, vtype_buf, &error);
+ mono_error_cleanup (&error);
g_free (vtype_buf);
} else {
char *name = mono_type_full_name (t);
static ErrorCode
decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
{
+ MonoError error;
ErrorCode err;
int type = decode_byte (buf, &buf, limit);
g_free (nullable_buf);
return err;
}
- mono_nullable_init (addr, mono_value_box (domain, mono_class_from_mono_type (targ), nullable_buf), mono_class_from_mono_type (t));
+ MonoObject *boxed = mono_value_box_checked (domain, mono_class_from_mono_type (targ), nullable_buf, &error);
+ if (!is_ok (&error)) {
+ mono_error_cleanup (&error);
+ return ERR_INVALID_OBJECT;
+ }
+ mono_nullable_init (addr, boxed, mono_class_from_mono_type (t));
g_free (nullable_buf);
*endbuf = buf;
return ERR_NONE;
GSList *cie_program;
FILE *fp;
const char *temp_prefix;
- gboolean emit_line, appending, collect_line_info;
+ gboolean emit_line;
GSList *line_info;
int cur_file_index;
};
* Create a DWARF writer object. WRITER is the underlying image writer this
* writer will emit to. IL_FILE is the file where IL code will be dumped to for
* methods which have no line number info. It can be NULL.
- * If APPENDING is TRUE, the output file will be in assembleable state after each
- * call to the _emit_ functions. This is used for XDEBUG. If APPENDING is FALSE,
- * a separate mono_dwarf_writer_close () call is needed to finish the emission of
- * debug information.
*/
MonoDwarfWriter*
-mono_dwarf_writer_create (MonoImageWriter *writer, FILE *il_file, int il_file_start_line, gboolean appending, gboolean emit_line_numbers)
+mono_dwarf_writer_create (MonoImageWriter *writer, FILE *il_file, int il_file_start_line, gboolean emit_line_numbers)
{
MonoDwarfWriter *w = g_new0 (MonoDwarfWriter, 1);
-
- /*
- * The appending flag is needed because we use subsections to order things in
- * the debug info, and:
- * - apple's assembler doesn't support them
- * - the binary writer has problems with subsections+alignment
- * So instead of subsections, we use the _close () function in AOT mode,
- * which writes out things in order.
- */
w->w = writer;
w->il_file = il_file;
w->il_file_line_index = il_file_start_line;
- w->appending = appending;
-
- if (appending)
- g_assert (mono_img_writer_subsections_supported (w->w));
-
- w->emit_line = TRUE;
-
- if (appending) {
- if (!mono_img_writer_subsections_supported (w->w))
- /* Can't emit line number info without subsections */
- w->emit_line = FALSE;
- } else {
- /* Collect line number info and emit it at once */
- w->collect_line_info = TRUE;
- }
- if (!emit_line_numbers) {
- w->emit_line = FALSE;
- w->collect_line_info = FALSE;
- }
+ w->emit_line = emit_line_numbers;
w->fp = mono_img_writer_get_fp (w->w);
w->temp_prefix = mono_img_writer_get_temp_label_prefix (w->w);
GSList *l;
GSList *info_list;
- g_assert (w->collect_line_info);
-
add_line_number_file_name (w, "<unknown>", 0, 0);
/* Collect files */
char *s, *build_info;
int i;
+ if (!w->emit_line) {
+ emit_section_change (w, ".debug_line", 0);
+ emit_label (w, ".Ldebug_line_section_start");
+ emit_label (w, ".Ldebug_line_start");
+ }
+
w->cie_program = base_unwind_program;
emit_section_change (w, ".debug_abbrev", 0);
emit_int32 (w, 0); /* .debug_abbrev offset */
emit_byte (w, sizeof (gpointer)); /* address size */
- if (mono_img_writer_subsections_supported (w->w) && w->appending) {
- /* Emit this into a separate section so it gets placed at the end */
- emit_section_change (w, ".debug_info", 1);
- emit_byte (w, 0); /* close COMPILE_UNIT */
- emit_label (w, ".Ldebug_info_end");
- emit_section_change (w, ".debug_info", 0);
- }
-
/* Compilation unit */
emit_uleb128 (w, ABBREV_COMPILE_UNIT);
build_info = mono_get_runtime_build_info ();
emit_pointer_value (w, 0);
emit_pointer_value (w, 0);
/* offset into .debug_line section */
- if (w->emit_line)
- emit_symbol_diff (w, ".Ldebug_line_start", ".Ldebug_line_section_start", 0);
- else
- emit_pointer_value (w, 0);
+ emit_symbol_diff (w, ".Ldebug_line_start", ".Ldebug_line_section_start", 0);
/* Base types */
for (i = 0; i < G_N_ELEMENTS (basic_types); ++i) {
void
mono_dwarf_writer_close (MonoDwarfWriter *w)
{
- if (!w->appending) {
- emit_section_change (w, ".debug_info", 0);
- emit_byte (w, 0); /* close COMPILE_UNIT */
- emit_label (w, ".Ldebug_info_end");
- }
+ emit_section_change (w, ".debug_info", 0);
+ emit_byte (w, 0); /* close COMPILE_UNIT */
+ emit_label (w, ".Ldebug_info_end");
- if (w->collect_line_info)
+ if (w->emit_line)
emit_all_line_number_info (w);
}
if (!prev_file_name || strcmp (loc->source_file, prev_file_name) != 0) {
/* Add an entry to the file table */
/* FIXME: Avoid duplicates */
- if (w->collect_line_info)
- file_index = get_line_number_file_name (w, loc->source_file) + 1;
- else
- file_index = emit_line_number_file_name (w, loc->source_file, 0, 0);
+ file_index = get_line_number_file_name (w, loc->source_file) + 1;
g_free (prev_file_name);
prev_file_name = g_strdup (loc->source_file);
w->fde_index ++;
}
- /* Emit line number info */
+ /* Save the information needed to emit the line number info later at once */
/* != could happen when using --regression */
if (debug_info && (debug_info->code_start == code)) {
- if (w->collect_line_info) {
- MethodLineNumberInfo *info;
-
- /* Save the information needed to emit the line number info later at once */
- info = g_new0 (MethodLineNumberInfo, 1);
- info->method = method;
- info->start_symbol = g_strdup (start_symbol);
- info->end_symbol = g_strdup (end_symbol);
- info->code = code;
- info->code_size = code_size;
- w->line_info = g_slist_prepend (w->line_info, info);
- } else {
- emit_line_number_info (w, method, start_symbol, end_symbol, code, code_size, debug_info);
- }
+ MethodLineNumberInfo *info;
+
+ info = g_new0 (MethodLineNumberInfo, 1);
+ info->method = method;
+ info->start_symbol = g_strdup (start_symbol);
+ info->end_symbol = g_strdup (end_symbol);
+ info->code = code;
+ info->code_size = code_size;
+ w->line_info = g_slist_prepend (w->line_info, info);
}
emit_line (w);
typedef struct _MonoDwarfWriter MonoDwarfWriter;
-MonoDwarfWriter* mono_dwarf_writer_create (MonoImageWriter *writer, FILE *il_file, int il_file_start_line, gboolean appending, gboolean emit_line_numbers);
+MonoDwarfWriter* mono_dwarf_writer_create (MonoImageWriter *writer, FILE *il_file, int il_file_start_line, gboolean emit_line_numbers);
void mono_dwarf_writer_destroy (MonoDwarfWriter *w);
guint64 dummy5, guint64 dummy6,
MonoContext *mctx, MonoObject *exc, gboolean rethrow)
{
+ MonoError error;
MonoContext ctx;
/* mctx is on the caller's stack */
memcpy (&ctx, mctx, sizeof (MonoContext));
- if (mono_object_isinst (exc, mono_defaults.exception_class)) {
+ if (mono_object_isinst_checked (exc, mono_defaults.exception_class, &error)) {
MonoException *mono_ex = (MonoException*)exc;
if (!rethrow) {
mono_ex->stack_trace = NULL;
mono_ex->trace_ips = NULL;
}
}
+ mono_error_assert_ok (&error);
/* adjust eip so that it point into the call instruction */
ctx.gregs [AMD64_RIP] --;
amd64_lea_membase (code, AMD64_RAX, AMD64_RSP, stack_size + sizeof(mgreg_t));
amd64_mov_membase_reg (code, AMD64_RSP, regs_offset + (AMD64_RSP * sizeof(mgreg_t)), X86_EAX, sizeof(mgreg_t));
/* Save IP */
- if (llvm_abs)
- amd64_alu_reg_reg (code, X86_XOR, AMD64_RAX, AMD64_RAX);
- else
- amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RSP, stack_size, sizeof(mgreg_t));
+ amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RSP, stack_size, sizeof(mgreg_t));
amd64_mov_membase_reg (code, AMD64_RSP, regs_offset + (AMD64_RIP * sizeof(mgreg_t)), AMD64_RAX, sizeof(mgreg_t));
/* Set arg1 == ctx */
amd64_lea_membase (code, AMD64_RAX, AMD64_RSP, ctx_offset);
if (resume_unwind) {
amd64_mov_membase_imm (code, AMD64_RSP, arg_offsets [2], 0, sizeof(mgreg_t));
} else if (corlib) {
- amd64_mov_membase_reg (code, AMD64_RSP, arg_offsets [2], AMD64_ARG_REG2, sizeof(mgreg_t));
if (llvm_abs)
- /*
- * The caller is LLVM code which passes the absolute address not a pc offset,
- * so compensate by passing 0 as 'rip' and passing the negated abs address as
- * the pc offset.
+ /*
+ * The caller doesn't pass in a pc/pc offset, instead we simply use the
+ * caller ip. Negate the pc adjustment done in mono_amd64_throw_corlib_exception ().
*/
- amd64_neg_membase (code, AMD64_RSP, arg_offsets [2]);
+ amd64_mov_membase_imm (code, AMD64_RSP, arg_offsets [2], 1, sizeof(mgreg_t));
+ else
+ amd64_mov_membase_reg (code, AMD64_RSP, arg_offsets [2], AMD64_ARG_REG2, sizeof(mgreg_t));
} else {
amd64_mov_membase_imm (code, AMD64_RSP, arg_offsets [2], rethrow, sizeof(mgreg_t));
}
void
mono_arm_throw_exception (MonoObject *exc, mgreg_t pc, mgreg_t sp, mgreg_t *int_regs, gdouble *fp_regs)
{
+ MonoError error;
MonoContext ctx;
gboolean rethrow = sp & 1;
memcpy (((guint8*)&ctx.regs) + (ARMREG_R4 * sizeof (mgreg_t)), int_regs, 8 * sizeof (mgreg_t));
memcpy (&ctx.fregs, fp_regs, sizeof (double) * 16);
- if (mono_object_isinst (exc, mono_defaults.exception_class)) {
+ if (mono_object_isinst_checked (exc, mono_defaults.exception_class, &error)) {
MonoException *mono_ex = (MonoException*)exc;
if (!rethrow) {
mono_ex->stack_trace = NULL;
mono_ex->trace_ips = NULL;
}
}
+ mono_error_assert_ok (&error);
mono_handle_exception (&ctx, exc);
mono_restore_context (&ctx);
g_assert_not_reached ();
}
void
-mono_arm_throw_exception_by_token (guint32 type_token, mgreg_t pc, mgreg_t sp, mgreg_t *int_regs, gdouble *fp_regs)
+mono_arm_throw_exception_by_token (guint32 ex_token_index, mgreg_t pc, mgreg_t sp, mgreg_t *int_regs, gdouble *fp_regs)
{
+ guint32 ex_token = MONO_TOKEN_TYPE_DEF | ex_token_index;
/* Clear thumb bit */
pc &= ~1;
- mono_arm_throw_exception ((MonoObject*)mono_exception_from_token (mono_defaults.corlib, type_token), pc, sp, int_regs, fp_regs);
+ mono_arm_throw_exception ((MonoObject*)mono_exception_from_token (mono_defaults.corlib, ex_token), pc, sp, int_regs, fp_regs);
}
void
/* exc is already in place in r0 */
if (corlib) {
/* The caller ip is already in R1 */
- if (llvm)
- /* Negate the ip adjustment done in mono_arm_throw_exception */
- ARM_ADD_REG_IMM8 (code, ARMREG_R1, ARMREG_R1, 4);
+ if (llvm) {
+ /*
+ * The address passed by llvm might point to before the call,
+ * thus outside the eh range recorded by llvm. Use the return
+ * address instead.
+ * FIXME: Do this on more platforms.
+ */
+ ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR); /* caller ip */
+ }
} else {
ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR); /* caller ip */
}
throw_exception (MonoObject *exc, guint64 rethrow)
{
unw_context_t unw_ctx;
+ MonoError error;
MonoContext ctx;
MonoJitInfo *ji;
unw_word_t ip, sp;
int res;
- if (mono_object_isinst (exc, mono_defaults.exception_class)) {
+ if (mono_object_isinst_checked (exc, mono_defaults.exception_class, &error)) {
MonoException *mono_ex = (MonoException*)exc;
if (!rethrow) {
mono_ex->stack_trace = NULL;
mono_ex->trace_ips = NULL;
}
}
+ mono_error_assert_ok (&error);
res = unw_getcontext (&unw_ctx);
g_assert (res == 0);
static void
throw_exception (MonoObject *exc, unsigned long eip, unsigned long esp, gboolean rethrow)
{
+ MonoError error;
MonoContext ctx;
#ifdef DEBUG_EXCEPTIONS
memset (&ctx.sc_fpregs, 0, sizeof (mips_freg) * MONO_SAVED_FREGS);
MONO_CONTEXT_SET_IP (&ctx, eip);
- if (mono_object_isinst (exc, mono_defaults.exception_class)) {
+ if (mono_object_isinst_checked (exc, mono_defaults.exception_class, &error)) {
MonoException *mono_ex = (MonoException*)exc;
if (!rethrow) {
mono_ex->stack_trace = NULL;
mono_ex->trace_ips = NULL;
}
}
+ mono_error_assert_ok (&error);
mono_handle_exception (&ctx, exc);
#ifdef DEBUG_EXCEPTIONS
g_print ("throw_exception: restore to pc=%p sp=%p fp=%p ctx=%p\n",
void
mono_ppc_throw_exception (MonoObject *exc, unsigned long eip, unsigned long esp, mgreg_t *int_regs, gdouble *fp_regs, gboolean rethrow)
{
+ MonoError error;
MonoContext ctx;
/* adjust eip so that it point into the call instruction */
memcpy (&ctx.regs, int_regs, sizeof (mgreg_t) * MONO_SAVED_GREGS);
memcpy (&ctx.fregs, fp_regs, sizeof (double) * MONO_SAVED_FREGS);
- if (mono_object_isinst (exc, mono_defaults.exception_class)) {
+ if (mono_object_isinst_checked (exc, mono_defaults.exception_class, &error)) {
MonoException *mono_ex = (MonoException*)exc;
if (!rethrow) {
mono_ex->stack_trace = NULL;
mono_ex->trace_ips = NULL;
}
}
+ mono_error_assert_ok (&error);
mono_handle_exception (&ctx, exc);
mono_restore_context (&ctx);
gulong *int_regs, gdouble *fp_regs, gint32 *acc_regs,
guint fpc, gboolean rethrow)
{
+ MonoError error;
MonoContext ctx;
int iReg;
MONO_CONTEXT_SET_BP (&ctx, sp);
MONO_CONTEXT_SET_IP (&ctx, ip);
- if (mono_object_isinst (exc, mono_defaults.exception_class)) {
+ if (mono_object_isinst_checked (exc, mono_defaults.exception_class, &error)) {
MonoException *mono_ex = (MonoException*)exc;
if (!rethrow) {
mono_ex->stack_trace = NULL;
mono_ex->trace_ips = NULL;
}
}
+ mono_error_assert_ok (&error);
// mono_arch_handle_exception (&ctx, exc, FALSE);
mono_handle_exception (&ctx, exc);
mono_restore_context(&ctx);
static void
throw_exception (MonoObject *exc, gpointer sp, gpointer ip, gboolean rethrow)
{
+ MonoError error;
MonoContext ctx;
static void (*restore_context) (MonoContext *);
gpointer *window;
ctx.ip = ip;
ctx.fp = (gpointer*)(MONO_SPARC_WINDOW_ADDR (sp) [sparc_i6 - 16]);
- if (mono_object_isinst (exc, mono_defaults.exception_class)) {
+ if (mono_object_isinst_checked (exc, mono_defaults.exception_class, &error)) {
MonoException *mono_ex = (MonoException*)exc;
if (!rethrow) {
mono_ex->stack_trace = NULL;
mono_ex->trace_ips = NULL;
}
}
+ mono_error_assert_ok (&error);
mono_handle_exception (&ctx, exc);
restore_context (&ctx);
mono_x86_throw_exception (mgreg_t *regs, MonoObject *exc,
mgreg_t eip, gboolean rethrow)
{
+ MonoError error;
MonoContext ctx;
ctx.esp = regs [X86_ESP];
g_assert ((ctx.esp % MONO_ARCH_FRAME_ALIGNMENT) == 0);
#endif
- if (mono_object_isinst (exc, mono_defaults.exception_class)) {
+ if (mono_object_isinst_checked (exc, mono_defaults.exception_class, &error)) {
MonoException *mono_ex = (MonoException*)exc;
if (!rethrow) {
mono_ex->stack_trace = NULL;
mono_ex->trace_ips = NULL;
}
}
+ mono_error_assert_ok (&error);
/* adjust eip so that it point into the call instruction */
ctx.eip -= 1;
return 0;
}
+ interface ISmallArg {
+ T foo<T> (string s1, string s2, string s3, string s4, string s5, string s6, string s7, string s8,
+ string s9, string s10, string s11, string s12, string s13, T t);
+ }
+
+ class SmallArgClass : ISmallArg {
+ public T foo<T> (string s1, string s2, string s3, string s4, string s5, string s6, string s7, string s8,
+ string s9, string s10, string s11, string s12, string s13, T t) {
+ return t;
+ }
+ }
+
+ public static int test_1_small_gsharedvt_stack_arg_ios () {
+ ISmallArg o = new SmallArgClass ();
+ return o.foo<int> ("", "", "", "", "", "", "", "", "", "", "", "", "", 1);
+ }
+
// Passing vtype normal arguments on the stack
public static int test_0_arm64_vtype_stack_args () {
IFoo3<EmptyStruct> o = (IFoo3<EmptyStruct>)Activator.CreateInstance (typeof (Foo3<>).MakeGenericType (new Type [] { typeof (EmptyStruct) }));
static void
asm_writer_emit_start (MonoImageWriter *acfg)
{
+#if defined(TARGET_ASM_APPLE)
+ fprintf (acfg->fp, ".subsections_via_symbols\n");
+#endif
}
static int
void
mono_helper_stelem_ref_check (MonoArray *array, MonoObject *val)
{
+ MonoError error;
if (!array) {
mono_set_pending_exception (mono_get_exception_null_reference ());
return;
}
- if (val && !mono_object_isinst (val, array->obj.vtable->klass->element_class)) {
+ if (val && !mono_object_isinst_checked (val, array->obj.vtable->klass->element_class, &error)) {
+ if (mono_error_set_pending_exception (&error))
+ return;
mono_set_pending_exception (mono_get_exception_array_type_mismatch ());
return;
}
MonoObject*
mono_object_castclass_unbox (MonoObject *obj, MonoClass *klass)
{
+ MonoError error;
MonoJitTlsData *jit_tls = NULL;
MonoClass *oklass;
oklass = obj->vtable->klass;
if ((klass->enumtype && oklass == klass->element_class) || (oklass->enumtype && klass == oklass->element_class))
return obj;
- if (mono_object_isinst (obj, klass))
+ if (mono_object_isinst_checked (obj, klass, &error))
return obj;
+ if (mono_error_set_pending_exception (&error))
+ return NULL;
if (mini_get_debug_options ()->better_cast_details) {
jit_tls->class_cast_from = oklass;
MonoObject*
mono_object_castclass_with_cache (MonoObject *obj, MonoClass *klass, gpointer *cache)
{
+ MonoError error;
MonoJitTlsData *jit_tls = NULL;
gpointer cached_vtable, obj_vtable;
if (cached_vtable == obj_vtable)
return obj;
- if (mono_object_isinst (obj, klass)) {
+ if (mono_object_isinst_checked (obj, klass, &error)) {
*cache = obj_vtable;
return obj;
}
+ if (mono_error_set_pending_exception (&error))
+ return NULL;
if (mini_get_debug_options ()->better_cast_details) {
jit_tls->class_cast_from = obj->vtable->klass;
MonoObject*
mono_object_isinst_with_cache (MonoObject *obj, MonoClass *klass, gpointer *cache)
{
+ MonoError error;
size_t cached_vtable, obj_vtable;
if (!obj)
return (cached_vtable & 0x1) ? NULL : obj;
}
- if (mono_object_isinst (obj, klass)) {
+ if (mono_object_isinst_checked (obj, klass, &error)) {
*cache = (gpointer)obj_vtable;
return obj;
} else {
+ if (mono_error_set_pending_exception (&error))
+ return NULL;
/*negative cache*/
*cache = (gpointer)(obj_vtable | 0x1);
return NULL;
/*
* Calling a non-vtype method with a vtype receiver, has to box.
*/
- *this_arg = mono_value_box (mono_domain_get (), klass, mp);
+ *this_arg = mono_value_box_checked (mono_domain_get (), klass, mp, error);
else if (klass->valuetype)
/*
* Calling a vtype method with a vtype receiver
INLINE_FAILURE ("class init");
if (!mono_runtime_class_init_full (vtable, &cfg->error)) {
mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
- g_assert_not_reached ();
goto exception_exit;
}
}
return code;
}
-#define REAL_PRINT_REG(text,reg) \
-mono_assert (reg >= 0); \
-amd64_push_reg (code, AMD64_RAX); \
-amd64_push_reg (code, AMD64_RDX); \
-amd64_push_reg (code, AMD64_RCX); \
-amd64_push_reg (code, reg); \
-amd64_push_imm (code, reg); \
-amd64_push_imm (code, text " %d %p\n"); \
-amd64_mov_reg_imm (code, AMD64_RAX, printf); \
-amd64_call_reg (code, AMD64_RAX); \
-amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 3*4); \
-amd64_pop_reg (code, AMD64_RCX); \
-amd64_pop_reg (code, AMD64_RDX); \
-amd64_pop_reg (code, AMD64_RAX);
-
/* benchmark and set based on cpu */
#define LOOP_ALIGNMENT 8
#define bb_is_loop_start(bb) ((bb)->loop_body_start && (bb)->nesting)
} else {
int creg;
+ cfg->param_area = MAX (cfg->param_area, 8);
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
creg = mono_alloc_ireg (cfg);
MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
} else {
int creg;
+ cfg->param_area = MAX (cfg->param_area, 8);
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
creg = mono_alloc_ireg (cfg);
MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
/* This should work for soft-float as well */
+ cfg->param_area = MAX (cfg->param_area, 8);
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
creg = mono_alloc_ireg (cfg);
mono_call_inst_add_outarg_reg (cfg, call, creg, ARMREG_R3, FALSE);
code = mono_arm_load_jumptable_entry_addr (code, jte, ARMREG_LR);
#else
ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
- ARM_B (code, 2);
+ ARM_B (code, 1);
*(gpointer*)code = &single_step_tramp;
code += 4;
*(gpointer*)code = breakpoint_tramp;
patch_info->ip.i = code - cfg->native_code;
ARM_BL (code, 0);
cfg->thunk_area += THUNK_SIZE;
- *(guint32*)(gpointer)code = exc_class->type_token;
+ *(guint32*)(gpointer)code = exc_class->type_token - MONO_TOKEN_TYPE_DEF;
code += 4;
#endif
break;
static gboolean
mono_handle_exception_internal_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filter_idx, MonoJitInfo **out_ji, MonoJitInfo **out_prev_ji, MonoObject *non_exception)
{
+ MonoError error;
MonoDomain *domain = mono_domain_get ();
MonoJitInfo *ji = NULL;
static int (*call_filter) (MonoContext *, gpointer) = NULL;
mono_ex = (MonoException*)obj;
initial_trace_ips = mono_ex->trace_ips;
- if (mono_object_isinst (obj, mono_defaults.exception_class)) {
+ if (mono_object_isinst_checked (obj, mono_defaults.exception_class, &error)) {
mono_ex = (MonoException*)obj;
initial_trace_ips = mono_ex->trace_ips;
} else {
+ mono_error_assert_ok (&error);
mono_ex = NULL;
}
}
}
- if (ei->flags == MONO_EXCEPTION_CLAUSE_NONE && mono_object_isinst (ex_obj, catch_class)) {
+ if (ei->flags == MONO_EXCEPTION_CLAUSE_NONE && mono_object_isinst_checked (ex_obj, catch_class, &error)) {
setup_stack_trace (mono_ex, dynamic_methods, initial_trace_ips, &trace_ips);
g_slist_free (dynamic_methods);
MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
return TRUE;
}
+ mono_error_cleanup (&error);
}
}
obj = (MonoObject *)mono_get_exception_null_reference ();
}
- if (!mono_object_isinst (obj, mono_defaults.exception_class)) {
+ if (!mono_object_isinst_checked (obj, mono_defaults.exception_class, &error)) {
+ mono_error_assert_ok (&error);
non_exception = obj;
obj = (MonoObject *)mono_get_exception_runtime_wrapped_checked (obj, &error);
mono_error_assert_ok (&error);
;
}
- if (mono_object_isinst (obj, mono_defaults.exception_class)) {
+ if (mono_object_isinst_checked (obj, mono_defaults.exception_class, &error)) {
mono_ex = (MonoException*)obj;
} else {
+ mono_error_assert_ok (&error);
mono_ex = NULL;
}
filter_idx ++;
}
+ mono_error_init (&error);
if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE &&
- mono_object_isinst (ex_obj, catch_class)) || filtered) {
+ mono_object_isinst_checked (ex_obj, catch_class, &error)) || filtered) {
/*
* This guards against the situation that we abort a thread that is executing a finally clause
* that was called by the EH machinery. It won't have a guard trampoline installed, so we must
return 0;
}
+ mono_error_cleanup (&error);
if (ei->flags == MONO_EXCEPTION_CLAUSE_FAULT) {
if (mono_trace_is_enabled () && mono_trace_eval (method))
g_print ("EXCEPTION: fault clause %d of %s\n", i, mono_method_full_name (method, TRUE));
MonoJitTlsData *jit_tls = mono_get_jit_tls ();
MonoException *mono_ex;
- if (!mono_object_isinst (ex, mono_defaults.exception_class)) {
+ if (!mono_object_isinst_checked (ex, mono_defaults.exception_class, &error)) {
+ mono_error_assert_ok (&error);
mono_ex = mono_get_exception_runtime_wrapped_checked (ex, &error);
mono_error_assert_ok (&error);
}
gint32
mono_llvm_match_exception (MonoJitInfo *jinfo, guint32 region_start, guint32 region_end, gpointer rgctx, MonoObject *this_obj)
{
+ MonoError error;
MonoJitTlsData *jit_tls = mono_get_jit_tls ();
MonoObject *exc;
gint32 index = -1;
catch_class = ei->data.catch_class;
if (catch_class->byval_arg.type == MONO_TYPE_VAR || catch_class->byval_arg.type == MONO_TYPE_MVAR || catch_class->byval_arg.type == MONO_TYPE_GENERICINST) {
- MonoError error;
MonoGenericContext context;
MonoType *inflated_type;
}
// FIXME: Handle edge cases handled in get_exception_catch_class
- if (ei->flags == MONO_EXCEPTION_CLAUSE_NONE && mono_object_isinst (exc, catch_class)) {
+ if (ei->flags == MONO_EXCEPTION_CLAUSE_NONE && mono_object_isinst_checked (exc, catch_class, &error)) {
index = ei->clause_index;
break;
- } else if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
+ } else
+ mono_error_assert_ok (&error);
+
+ if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
g_assert_not_reached ();
}
}
#define __STDC_CONSTANT_MACROS
#endif
-#include "llvm-c/Core.h"
-#include "llvm-c/ExecutionEngine.h"
#include "llvm-c/BitWriter.h"
#include "llvm-c/Analysis.h"
MonoClass *exc_class;
LLVMValueRef args [2];
LLVMValueRef callee;
+ gboolean no_pc = FALSE;
+
+ if (IS_TARGET_AMD64)
+ /* Some platforms don't require the pc argument */
+ no_pc = TRUE;
ex_bb = gen_bb (ctx, "EX_BB");
if (ctx->llvm_only)
LLVMTypeRef sig;
const char *icall_name;
- sig = LLVMFunctionType2 (LLVMVoidType (), LLVMInt32Type (), LLVMPointerType (LLVMInt8Type (), 0), FALSE);
+ if (no_pc)
+ sig = LLVMFunctionType1 (LLVMVoidType (), LLVMInt32Type (), FALSE);
+ else
+ sig = LLVMFunctionType2 (LLVMVoidType (), LLVMInt32Type (), LLVMPointerType (LLVMInt8Type (), 0), FALSE);
icall_name = "llvm_throw_corlib_exception_abs_trampoline";
if (ctx->cfg->compile_aot) {
}
}
- if (IS_TARGET_X86 || IS_TARGET_AMD64)
- args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token - MONO_TOKEN_TYPE_DEF, FALSE);
- else
- args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token, FALSE);
+ args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token - MONO_TOKEN_TYPE_DEF, FALSE);
/*
* The LLVM mono branch contains changes so a block address can be passed as an
* argument to a call.
*/
- args [1] = LLVMBlockAddress (ctx->lmethod, ex_bb);
- emit_call (ctx, bb, &builder, callee, args, 2);
+ if (no_pc) {
+ emit_call (ctx, bb, &builder, callee, args, 1);
+ } else {
+ args [1] = LLVMBlockAddress (ctx->lmethod, ex_bb);
+ emit_call (ctx, bb, &builder, callee, args, 2);
+ }
LLVMBuildUnreachable (builder);
MonoVTable *vtable;
mono_jit_stats.methods_lookups++;
- vtable = mono_class_vtable (domain, method->klass);
+ vtable = mono_class_vtable_full (domain, method->klass, error);
+ if (!is_ok (error))
+ return NULL;
g_assert (vtable);
if (!mono_runtime_class_init_full (vtable, error))
return NULL;
mono_error_set_exception_instance (error, (MonoException*) *exc);
if (sig->ret->type != MONO_TYPE_VOID && info->ret_box_class)
- return mono_value_box (domain, info->ret_box_class, retval);
+ return mono_value_box_checked (domain, info->ret_box_class, retval, error);
else
return *(MonoObject**)retval;
}
mono_arch_finish_dyn_call (info->dyn_call_info, buf);
if (info->ret_box_class)
- return mono_value_box (domain, info->ret_box_class, retval);
+ return mono_value_box_checked (domain, info->ret_box_class, retval, error);
else
return *(MonoObject**)retval;
}
return res;
}
-/*
- * Precompute data to speed up mono_delegate_trampoline ().
- * METHOD might be NULL.
- */
-static MonoDelegateTrampInfo*
-create_delegate_trampoline_data (MonoDomain *domain, MonoClass *klass, MonoMethod *method)
-{
- MonoDelegateTrampInfo *tramp_data;
- MonoMethod *invoke;
- MonoError err;
-
- // Precompute the delegate invoke impl and pass it to the delegate trampoline
- invoke = mono_get_delegate_invoke (klass);
- g_assert (invoke);
-
- tramp_data = (MonoDelegateTrampInfo *)mono_domain_alloc0 (domain, sizeof (MonoDelegateTrampInfo));
- tramp_data->invoke = invoke;
- tramp_data->invoke_sig = mono_method_signature (invoke);
- tramp_data->impl_this = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke), TRUE);
- tramp_data->impl_nothis = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke), FALSE);
- tramp_data->method = method;
- if (method) {
- mono_error_init (&err);
- tramp_data->sig = mono_method_signature_checked (method, &err);
- tramp_data->need_rgctx_tramp = mono_method_needs_static_rgctx_invoke (method, FALSE);
- }
-
- return tramp_data;
-}
-
/**
* mono_delegate_trampoline:
*
/*
* mono_create_delegate_trampoline_info:
*
- * Create a delegate trampoline for the KLASS+METHOD pair.
+ * Create a trampoline info structure for the KLASS+METHOD pair.
*/
MonoDelegateTrampInfo*
mono_create_delegate_trampoline_info (MonoDomain *domain, MonoClass *klass, MonoMethod *method)
{
+ MonoMethod *invoke;
+ MonoError error;
MonoDelegateTrampInfo *tramp_info;
MonoClassMethodPair pair, *dpair;
guint32 code_size = 0;
if (tramp_info)
return tramp_info;
- tramp_info = create_delegate_trampoline_data (domain, klass, method);
+ invoke = mono_get_delegate_invoke (klass);
+ g_assert (invoke);
+ tramp_info = (MonoDelegateTrampInfo *)mono_domain_alloc0 (domain, sizeof (MonoDelegateTrampInfo));
+ tramp_info->invoke = invoke;
+ tramp_info->invoke_sig = mono_method_signature (invoke);
+ tramp_info->impl_this = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke), TRUE);
+ tramp_info->impl_nothis = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke), FALSE);
+ tramp_info->method = method;
+ if (method) {
+ mono_error_init (&error);
+ tramp_info->sig = mono_method_signature_checked (method, &error);
+ tramp_info->need_rgctx_tramp = mono_method_needs_static_rgctx_invoke (method, FALSE);
+ }
tramp_info->invoke_impl = mono_create_specific_trampoline (tramp_info, MONO_TRAMPOLINE_DELEGATE, domain, &code_size);
g_assert (code_size);
return code;
}
-#define REAL_PRINT_REG(text,reg) \
-mono_assert (reg >= 0); \
-x86_push_reg (code, X86_EAX); \
-x86_push_reg (code, X86_EDX); \
-x86_push_reg (code, X86_ECX); \
-x86_push_reg (code, reg); \
-x86_push_imm (code, reg); \
-x86_push_imm (code, text " %d %p\n"); \
-x86_mov_reg_imm (code, X86_EAX, printf); \
-x86_call_reg (code, X86_EAX); \
-x86_alu_reg_imm (code, X86_ADD, X86_ESP, 3*4); \
-x86_pop_reg (code, X86_ECX); \
-x86_pop_reg (code, X86_EDX); \
-x86_pop_reg (code, X86_EAX);
-
-/* REAL_PRINT_REG does not appear to be used, and was not adapted to work with Native Client. */
-#ifdef __native__client_codegen__
-#define REAL_PRINT_REG(text, reg) g_assert_not_reached()
-#endif
-
/* benchmark and set based on cpu */
#define LOOP_ALIGNMENT 8
#define bb_is_loop_start(bb) ((bb)->loop_body_start && (bb)->nesting)
ARM_STR_IMM (code, ARMREG_R0, ARMREG_FP, MONO_STRUCT_OFFSET (MonoContext, regs) + 4 * ARMREG_SP);
/* make ctx.eip hold the address of the call. */
- ARM_SUB_REG_IMM8 (code, ARMREG_LR, ARMREG_LR, 4);
+ //ARM_SUB_REG_IMM8 (code, ARMREG_LR, ARMREG_LR, 4);
ARM_STR_IMM (code, ARMREG_LR, ARMREG_FP, MONO_STRUCT_OFFSET (MonoContext, pc));
/* r0 now points to the MonoContext */
g_free (cached);
}
-
g_free (cached_info);
+
+ for (GSList *cursor = cached_info_list; cursor != NULL; cursor = cursor->next)
+ g_free (cursor->data);
+
+ g_slist_free (cached_info_list);
}
/*
mono_memory_barrier ();
- cached_info = new_table;
-
cached_info_list = g_slist_prepend (cached_info_list, cached_info);
+ cached_info = new_table;
+
cached_info_size *= 2;
}
mono_img_writer_emit_start (w);
- xdebug_writer = mono_dwarf_writer_create (w, il_file, 0, TRUE, TRUE);
+ xdebug_writer = mono_dwarf_writer_create (w, il_file, 0, TRUE);
/* Emit something so the file has a text segment */
mono_img_writer_emit_section_change (w, ".text", 0);
if (!il_file)
il_file = fopen ("xdb.il", "w");
- dw = mono_dwarf_writer_create (w, il_file, il_file_line_index, FALSE, TRUE);
+ dw = mono_dwarf_writer_create (w, il_file, il_file_line_index, TRUE);
mono_dwarf_writer_emit_base_info (dw, "JITted code", mono_unwind_get_cie_program ());
sgen-los.c \
sgen-major-copy-object.h \
sgen-marksweep-drain-gray-stack.h \
- sgen-marksweep-scan-object-concurrent.h \
sgen-marksweep.c \
sgen-memory-governor.c \
sgen-memory-governor.h \
sgen-pinning.h \
sgen-pointer-queue.c \
sgen-pointer-queue.h \
+ sgen-array-list.h \
+ sgen-array-list.c \
sgen-protocol-def.h \
sgen-protocol.c \
sgen-protocol.h \
--- /dev/null
+/*
+ * sgen-array-list.c: A pointer array list that doesn't require reallocs
+ *
+ * Copyright (C) 2016 Xamarin Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License 2.0 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License 2.0 along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifdef HAVE_SGEN_GC
+
+#include <string.h>
+
+#include "mono/sgen/sgen-gc.h"
+#include "mono/sgen/sgen-array-list.h"
+
+static void
+sgen_array_list_grow (SgenArrayList *array, guint32 old_capacity)
+{
+ const guint32 new_bucket = sgen_array_list_index_bucket (old_capacity);
+ const guint32 growth = sgen_array_list_bucket_size (new_bucket);
+ const guint32 new_capacity = old_capacity + growth;
+ const guint32 new_bucket_size = sizeof (**array->entries) * growth;
+ gpointer *entries;
+ if (array->capacity >= new_capacity)
+ return;
+ if (array->mem_type != -1)
+ entries = (gpointer*) sgen_alloc_internal_dynamic (new_bucket_size, array->mem_type, TRUE);
+ else
+ entries = (gpointer*) g_malloc0 (new_bucket_size);
+ if (array->bucket_alloc_callback)
+ array->bucket_alloc_callback (entries, new_bucket_size, TRUE);
+ /*
+ * The zeroing of the newly allocated bucket must be complete before storing
+ * the new bucket pointer.
+ */
+ mono_memory_write_barrier ();
+ if (InterlockedCompareExchangePointer ((volatile gpointer *)&array->entries [new_bucket], entries, NULL) == NULL) {
+ /*
+ * It must not be the case that we succeeded in setting the bucket
+ * pointer, while someone else succeeded in changing the capacity.
+ */
+ if (InterlockedCompareExchange ((volatile gint32 *)&array->capacity, new_capacity, old_capacity) != old_capacity)
+ g_assert_not_reached ();
+ array->slot_hint = old_capacity;
+ return;
+ }
+ /* Someone beat us to the allocation. */
+ if (array->bucket_alloc_callback)
+ array->bucket_alloc_callback (entries, new_bucket_size, FALSE);
+ if (array->mem_type != -1)
+ sgen_free_internal_dynamic (entries, new_bucket_size, array->mem_type);
+ else
+ g_free (entries);
+}
+
+static guint32
+sgen_array_list_find_unset (SgenArrayList *array, guint32 capacity)
+{
+ if (!array->is_slot_set_func) {
+ guint32 next_slot = array->next_slot;
+ /* We can't lookup empty slots, use next_slot */
+ if (next_slot < capacity)
+ return next_slot;
+ } else {
+ guint32 slot_hint = array->slot_hint;
+ guint32 index;
+ volatile gpointer *slot;
+
+ SGEN_ARRAY_LIST_FOREACH_SLOT_RANGE(array, slot_hint, capacity, slot, index) {
+ if (!array->is_slot_set_func (slot))
+ return index;
+ } SGEN_ARRAY_LIST_END_FOREACH_SLOT_RANGE;
+
+ SGEN_ARRAY_LIST_FOREACH_SLOT_RANGE (array, 0, slot_hint, slot, index) {
+ if (!array->is_slot_set_func (slot))
+ return index;
+ } SGEN_ARRAY_LIST_END_FOREACH_SLOT_RANGE;
+ }
+
+ return -1;
+}
+
+static void
+sgen_array_list_update_next_slot (SgenArrayList *array, guint32 new_index)
+{
+ if (!array->set_slot_func) {
+ /*
+ * If we don't have a custom setter it means we don't have thread
+ * safety requirements.
+ */
+ if (new_index >= array->next_slot)
+ array->next_slot = new_index + 1;
+ } else {
+ guint32 old_next_slot;
+ /* Thread safe update */
+ do {
+ old_next_slot = array->next_slot;
+ if (new_index < old_next_slot)
+ break;
+ } while (InterlockedCompareExchange ((volatile gint32 *)&array->next_slot, new_index + 1, old_next_slot) != old_next_slot);
+ }
+}
+
+guint32
+sgen_array_list_add (SgenArrayList *array, gpointer ptr, int data, gboolean increase_size_before_set)
+{
+ guint32 index, capacity;
+ volatile gpointer *slot;
+
+ if (!array->capacity)
+ sgen_array_list_grow (array, 0);
+retry:
+ capacity = array->capacity;
+ index = sgen_array_list_find_unset (array, capacity);
+ if (index == -1) {
+ sgen_array_list_grow (array, capacity);
+ goto retry;
+ }
+ array->slot_hint = index;
+
+ if (increase_size_before_set) {
+ sgen_array_list_update_next_slot (array, index);
+ mono_memory_write_barrier ();
+ }
+
+ slot = sgen_array_list_get_slot (array, index);
+ if (array->set_slot_func) {
+ if (!array->set_slot_func (slot, ptr, data))
+ goto retry;
+ } else {
+ *slot = ptr;
+ }
+
+ if (!increase_size_before_set) {
+ mono_memory_write_barrier ();
+ sgen_array_list_update_next_slot (array, index);
+ }
+
+ return index;
+}
+
+/*
+ * Removes all NULL pointers from the array. Not thread safe
+ */
+void
+sgen_array_list_remove_nulls (SgenArrayList *array)
+{
+ guint32 start = 0;
+ volatile gpointer *slot;
+
+ SGEN_ARRAY_LIST_FOREACH_SLOT (array, slot) {
+ if (*slot)
+ *sgen_array_list_get_slot (array, start++) = *slot;
+ } SGEN_ARRAY_LIST_END_FOREACH_SLOT;
+
+ mono_memory_write_barrier ();
+ array->next_slot = start;
+}
+
+/*
+ * Does a linear search through the pointer array to find `ptr`. Returns the index if
+ * found, otherwise (guint32)-1.
+ */
+guint32
+sgen_array_list_find (SgenArrayList *array, gpointer ptr)
+{
+ volatile gpointer *slot;
+
+ SGEN_ARRAY_LIST_FOREACH_SLOT (array, slot) {
+ if (*slot == ptr)
+ return __index;
+ } SGEN_ARRAY_LIST_END_FOREACH_SLOT;
+ return (guint32)-1;
+}
+
+#endif
--- /dev/null
+/*
+ * sgen-array-list.h: A pointer array that doesn't use reallocs.
+ *
+ * Copyright (C) 2016 Xamarin Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License 2.0 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License 2.0 along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MONO_SGEN_ARRAY_LIST_H__
+#define __MONO_SGEN_ARRAY_LIST_H__
+
+#include <glib.h>
+
+#define SGEN_ARRAY_LIST_BUCKETS (32)
+#define SGEN_ARRAY_LIST_MIN_BUCKET_BITS (5)
+#define SGEN_ARRAY_LIST_MIN_BUCKET_SIZE (1 << SGEN_ARRAY_LIST_MIN_BUCKET_BITS)
+
+typedef void (*SgenArrayListBucketAllocCallback) (gpointer *bucket, guint32 new_bucket_size, gboolean alloc);
+typedef gboolean (*SgenArrayListIsSlotSetFunc) (volatile gpointer *slot);
+typedef gboolean (*SgenArrayListSetSlotFunc) (volatile gpointer *slot, gpointer ptr, int data);
+
+/*
+ * 'entries' is an array of pointers to buckets of increasing size. The first
+ * bucket has size 'MIN_BUCKET_SIZE', and each bucket is twice the size of the
+ * previous, i.e.:
+ *
+ * |-------|-- MIN_BUCKET_SIZE
+ * [0] -> xxxxxxxx
+ * [1] -> xxxxxxxxxxxxxxxx
+ * [2] -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ * ...
+ *
+ * 'slot_hint' denotes the position of the last allocation, so that the
+ * whole array needn't be searched on every allocation.
+ *
+ * The size of the spine, 'SGEN_ARRAY_LIST_BUCKETS', is chosen so
+ * that the maximum number of entries is no less than G_MAXUINT32.
+ */
+
+typedef struct {
+ volatile gpointer *volatile entries [SGEN_ARRAY_LIST_BUCKETS];
+ volatile guint32 capacity;
+ volatile guint32 slot_hint;
+ volatile guint32 next_slot;
+ SgenArrayListBucketAllocCallback bucket_alloc_callback;
+ SgenArrayListIsSlotSetFunc is_slot_set_func;
+ SgenArrayListSetSlotFunc set_slot_func;
+ int mem_type; /* sgen internal mem type or -1 for malloc allocation */
+} SgenArrayList;
+
+/*
+ * Computes floor(log2(index + MIN_BUCKET_SIZE)) - 1, giving the index
+ * of the bucket containing a slot.
+ */
+static inline guint32
+sgen_array_list_index_bucket (guint32 index)
+{
+#ifdef __GNUC__
+ return CHAR_BIT * sizeof (index) - __builtin_clz (index + SGEN_ARRAY_LIST_MIN_BUCKET_SIZE) - 1 - SGEN_ARRAY_LIST_MIN_BUCKET_BITS;
+#else
+ guint count = 0;
+ index += SGEN_ARRAY_LIST_MIN_BUCKET_SIZE;
+ while (index) {
+ ++count;
+ index >>= 1;
+ }
+ return count - 1 - SGEN_ARRAY_LIST_MIN_BUCKET_BITS;
+#endif
+}
+
+static inline guint32
+sgen_array_list_bucket_size (guint32 index)
+{
+ return 1 << (index + SGEN_ARRAY_LIST_MIN_BUCKET_BITS);
+}
+
+static inline void
+sgen_array_list_bucketize (guint32 index, guint32 *bucket, guint32 *offset)
+{
+ *bucket = sgen_array_list_index_bucket (index);
+ *offset = index - sgen_array_list_bucket_size (*bucket) + SGEN_ARRAY_LIST_MIN_BUCKET_SIZE;
+}
+
+static inline volatile gpointer *
+sgen_array_list_get_slot (SgenArrayList *array, guint32 index)
+{
+ guint32 bucket, offset;
+
+ SGEN_ASSERT (0, index < array->capacity, "Why are we accessing an entry that is not allocated");
+
+ sgen_array_list_bucketize (index, &bucket, &offset);
+ return &(array->entries [bucket] [offset]);
+}
+
+#define SGEN_ARRAY_LIST_INIT(bucket_alloc_callback, is_slot_set_func, set_slot_func, mem_type) { { NULL }, 0, 0, 0, (bucket_alloc_callback), (is_slot_set_func), (set_slot_func), (mem_type) }
+
+#define SGEN_ARRAY_LIST_FOREACH_SLOT(array, slot) { \
+ guint32 __bucket, __offset; \
+ const guint32 __max_bucket = sgen_array_list_index_bucket ((array)->capacity); \
+ guint32 __index = 0; \
+ const guint32 __next_slot = (array)->next_slot; \
+ for (__bucket = 0; __bucket < __max_bucket; ++__bucket) { \
+ volatile gpointer *__entries = (array)->entries [__bucket]; \
+ for (__offset = 0; __offset < sgen_array_list_bucket_size (__bucket); ++__offset, ++__index) { \
+ if (__index >= __next_slot) \
+ break; \
+ slot = &__entries [__offset];
+
+#define SGEN_ARRAY_LIST_END_FOREACH_SLOT } } }
+
+#define SGEN_ARRAY_LIST_FOREACH_SLOT_RANGE(array, begin, end, slot, index) { \
+ for (index = (begin); index < (end); index++) { \
+ guint32 __bucket, __offset; \
+ volatile gpointer *__entries; \
+ sgen_array_list_bucketize (index, &__bucket, &__offset); \
+ __entries = (array)->entries [__bucket]; \
+ slot = &__entries [__offset];
+
+#define SGEN_ARRAY_LIST_END_FOREACH_SLOT_RANGE } }
+
+guint32 sgen_array_list_add (SgenArrayList *array, gpointer ptr, int data, gboolean increase_size_before_set);
+guint32 sgen_array_list_find (SgenArrayList *array, gpointer ptr);
+void sgen_array_list_remove_nulls (SgenArrayList *array);
+
+#endif
*out_num_cards = num_cards;
}
+/* Preclean cards and saves the cards that need to be scanned afterwards in cards_preclean */
+void
+sgen_card_table_preclean_mod_union (guint8 *cards, guint8 *cards_preclean, size_t num_cards)
+{
+ size_t i;
+
+ memcpy (cards_preclean, cards, num_cards);
+ for (i = 0; i < num_cards; i++) {
+ if (cards_preclean [i]) {
+ cards [i] = 0;
+ }
+ }
+ /*
+ * When precleaning we need to make sure the card cleaning
+ * takes place before the object is scanned. If we don't
+ * do this we could finish scanning the object and, before
+ * the cleaning of the card takes place, another thread
+ * could dirty the object, mark the mod_union card only for
+ * us to clean it back, without scanning the object again.
+ */
+ mono_memory_barrier ();
+}
+
#ifdef SGEN_HAVE_OVERLAPPING_CARDS
static void
sgen_card_table_clear_cards ();
#endif
SGEN_TV_GETTIME (atv);
- sgen_get_major_collector ()->scan_card_table (FALSE, ctx);
+ sgen_get_major_collector ()->scan_card_table (CARDTABLE_SCAN_GLOBAL, ctx);
SGEN_TV_GETTIME (btv);
last_major_scan_time = SGEN_TV_ELAPSED (atv, btv);
major_card_scan_time += last_major_scan_time;
- sgen_los_scan_card_table (FALSE, ctx);
+ sgen_los_scan_card_table (CARDTABLE_SCAN_GLOBAL, ctx);
SGEN_TV_GETTIME (atv);
last_los_scan_time = SGEN_TV_ELAPSED (btv, atv);
los_card_scan_time += last_los_scan_time;
#endif
void
-sgen_cardtable_scan_object (GCObject *obj, mword block_obj_size, guint8 *cards, gboolean mod_union, ScanCopyContext ctx)
+sgen_cardtable_scan_object (GCObject *obj, mword block_obj_size, guint8 *cards, ScanCopyContext ctx)
{
HEAVY_STAT (++large_objects);
- if (sgen_client_cardtable_scan_object (obj, block_obj_size, cards, mod_union, ctx))
+ if (sgen_client_cardtable_scan_object (obj, block_obj_size, cards, ctx))
return;
HEAVY_STAT (++bloby_objects);
void* sgen_card_table_align_pointer (void *ptr);
void sgen_card_table_mark_range (mword address, mword size);
void sgen_cardtable_scan_object (GCObject *obj, mword obj_size, guint8 *cards,
- gboolean mod_union, ScanCopyContext ctx);
+ ScanCopyContext ctx);
gboolean sgen_card_table_get_card_data (guint8 *dest, mword address, mword cards);
void sgen_card_table_update_mod_union_from_cards (guint8 *dest, guint8 *start_card, size_t num_cards);
void sgen_card_table_update_mod_union (guint8 *dest, char *obj, mword obj_size, size_t *out_num_cards);
+void sgen_card_table_preclean_mod_union (guint8 *cards, guint8 *cards_preclean, size_t num_cards);
guint8* sgen_get_card_table_configuration (int *shift_bits, gpointer *mask);
* parts of the object based on which cards are marked, do so and return TRUE. Otherwise,
* return FALSE.
*/
-gboolean sgen_client_cardtable_scan_object (GCObject *obj, mword block_obj_size, guint8 *cards, gboolean mod_union, ScanCopyContext ctx);
+gboolean sgen_client_cardtable_scan_object (GCObject *obj, mword block_obj_size, guint8 *cards, ScanCopyContext ctx);
/*
* Called after nursery objects have been pinned. No action is necessary.
static gboolean do_dump_nursery_content = FALSE;
static gboolean enable_nursery_canaries = FALSE;
+static gboolean precleaning_enabled = TRUE;
+
#ifdef HEAVY_STATISTICS
guint64 stat_objects_alloced_degraded = 0;
guint64 stat_bytes_alloced_degraded = 0;
sgen_client_clear_togglerefs (start_addr, end_addr, ctx);
TV_GETTIME (btv);
- SGEN_LOG (2, "Finalize queue handling scan for %s generation: %ld usecs %d ephemeron rounds", generation_name (generation), TV_ELAPSED (atv, btv), ephemeron_rounds);
+ SGEN_LOG (2, "Finalize queue handling scan for %s generation: %lld usecs %d ephemeron rounds", generation_name (generation), TV_ELAPSED (atv, btv), ephemeron_rounds);
/*
* handle disappearing links
ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
g_assert (concurrent_collection_in_progress);
- major_collector.scan_card_table (TRUE, ctx);
+ major_collector.scan_card_table (CARDTABLE_SCAN_MOD_UNION, ctx);
}
static void
ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
g_assert (concurrent_collection_in_progress);
- sgen_los_scan_card_table (TRUE, ctx);
+ sgen_los_scan_card_table (CARDTABLE_SCAN_MOD_UNION, ctx);
+}
+
+static void
+job_mod_union_preclean (void *worker_data_untyped, SgenThreadPoolJob *job)
+{
+ WorkerData *worker_data = (WorkerData *)worker_data_untyped;
+ ScanJob *job_data = (ScanJob*)job;
+ ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
+
+ g_assert (concurrent_collection_in_progress);
+
+ major_collector.scan_card_table (CARDTABLE_SCAN_MOD_UNION_PRECLEAN, ctx);
+ sgen_los_scan_card_table (CARDTABLE_SCAN_MOD_UNION_PRECLEAN, ctx);
}
static void
TV_GETTIME (atv);
time_minor_pinning += TV_ELAPSED (btv, atv);
- SGEN_LOG (2, "Finding pinned pointers: %zd in %ld usecs", sgen_get_pinned_count (), TV_ELAPSED (btv, atv));
+ SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), TV_ELAPSED (btv, atv));
SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan remset", job_remembered_set_scan, sizeof (ScanJob));
/* we don't have complete write barrier yet, so we scan all the old generation sections */
TV_GETTIME (btv);
time_minor_scan_remsets += TV_ELAPSED (atv, btv);
- SGEN_LOG (2, "Old generation scan: %ld usecs", TV_ELAPSED (atv, btv));
+ SGEN_LOG (2, "Old generation scan: %lld usecs", TV_ELAPSED (atv, btv));
sgen_pin_stats_print_class_stats ();
sgen_client_binary_protocol_reclaim_end (GENERATION_NURSERY);
TV_GETTIME (btv);
time_minor_fragment_creation += TV_ELAPSED (atv, btv);
- SGEN_LOG (2, "Fragment creation: %ld usecs, %lu bytes available", TV_ELAPSED (atv, btv), (unsigned long)fragment_total);
+ SGEN_LOG (2, "Fragment creation: %lld usecs, %lu bytes available", TV_ELAPSED (atv, btv), (unsigned long)fragment_total);
if (consistency_check_at_minor_collection)
sgen_check_major_refs ();
TV_GETTIME (btv);
time_major_pinning += TV_ELAPSED (atv, btv);
- SGEN_LOG (2, "Finding pinned pointers: %zd in %ld usecs", sgen_get_pinned_count (), TV_ELAPSED (atv, btv));
+ SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), TV_ELAPSED (atv, btv));
SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
major_collector.init_to_space ();
+ SGEN_ASSERT (0, sgen_workers_all_done (), "Why are the workers not done when we start or finish a major collection?");
/*
* The concurrent collector doesn't move objects, neither on
* the major heap nor in the nursery, so we can mark even
* collector we start the workers after pinning.
*/
if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
- SGEN_ASSERT (0, sgen_workers_all_done (), "Why are the workers not done when we start or finish a major collection?");
- sgen_workers_start_all_workers (object_ops);
+ if (precleaning_enabled) {
+ ScanJob *sj;
+ /* Mod union preclean job */
+ sj = (ScanJob*)sgen_thread_pool_job_alloc ("preclean mod union cardtable", job_mod_union_preclean, sizeof (ScanJob));
+ sj->ops = object_ops;
+ sgen_workers_start_all_workers (object_ops, &sj->job);
+ } else {
+ sgen_workers_start_all_workers (object_ops, NULL);
+ }
gray_queue_enable_redirect (WORKERS_DISTRIBUTE_GRAY_QUEUE);
} else if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
if (sgen_workers_have_idle_work ()) {
- sgen_workers_start_all_workers (object_ops);
+ sgen_workers_start_all_workers (object_ops, NULL);
sgen_workers_join ();
}
}
continue;
}
+ if (!strcmp (opt, "precleaning")) {
+ precleaning_enabled = TRUE;
+ continue;
+ }
+ if (!strcmp (opt, "no-precleaning")) {
+ precleaning_enabled = FALSE;
+ continue;
+ }
+
if (major_collector.handle_gc_param && major_collector.handle_gc_param (opt))
continue;
typedef void (*CopyOrMarkObjectFunc) (GCObject**, SgenGrayQueue*);
typedef void (*ScanObjectFunc) (GCObject *obj, SgenDescriptor desc, SgenGrayQueue*);
typedef void (*ScanVTypeFunc) (GCObject *full_object, char *start, SgenDescriptor desc, SgenGrayQueue* BINARY_PROTOCOL_ARG (size_t size));
+typedef void (*ScanPtrFieldFunc) (GCObject *obj, GCObject **ptr, SgenGrayQueue* queue);
typedef gboolean (*DrainGrayStackFunc) (SgenGrayQueue *queue);
typedef struct {
CopyOrMarkObjectFunc copy_or_mark_object;
ScanObjectFunc scan_object;
ScanVTypeFunc scan_vtype;
+ ScanPtrFieldFunc scan_ptr_field;
/* Drain stack optimized for the above functions */
DrainGrayStackFunc drain_gray_stack;
/*FIXME add allocation function? */
size_t num_unique_scanned_objects;
} ScannedObjectCounts;
+typedef enum {
+ CARDTABLE_SCAN_GLOBAL = 0,
+ CARDTABLE_SCAN_MOD_UNION = 1,
+ CARDTABLE_SCAN_MOD_UNION_PRECLEAN = CARDTABLE_SCAN_MOD_UNION | 2,
+} CardTableScanType;
+
typedef struct _SgenMajorCollector SgenMajorCollector;
struct _SgenMajorCollector {
size_t section_size;
void (*free_non_pinned_object) (GCObject *obj, size_t size);
void (*pin_objects) (SgenGrayQueue *queue);
void (*pin_major_object) (GCObject *obj, SgenGrayQueue *queue);
- void (*scan_card_table) (gboolean mod_union, ScanCopyContext ctx);
+ void (*scan_card_table) (CardTableScanType scan_type, ScanCopyContext ctx);
void (*iterate_live_block_ranges) (sgen_cardtable_block_callback callback);
void (*update_cardtable_mod_union) (void);
void (*init_to_space) (void);
gboolean sgen_ptr_is_in_los (char *ptr, char **start);
void sgen_los_iterate_objects (IterateObjectCallbackFunc cb, void *user_data);
void sgen_los_iterate_live_block_ranges (sgen_cardtable_block_callback callback);
-void sgen_los_scan_card_table (gboolean mod_union, ScanCopyContext ctx);
+void sgen_los_scan_card_table (CardTableScanType scan_type, ScanCopyContext ctx);
void sgen_los_update_cardtable_mod_union (void);
void sgen_los_count_cards (long long *num_total_cards, long long *num_marked_cards);
gboolean sgen_los_is_valid_object (char *object);
#include "mono/sgen/sgen-gc.h"
#include "mono/sgen/sgen-client.h"
+#include "mono/sgen/sgen-array-list.h"
#include "mono/utils/mono-membar.h"
#ifdef HEAVY_STATISTICS
static volatile guint32 stat_gc_handles_max_allocated = 0;
#endif
-#define BUCKETS (32 - MONO_GC_HANDLE_TYPE_SHIFT)
-#define MIN_BUCKET_BITS (5)
-#define MIN_BUCKET_SIZE (1 << MIN_BUCKET_BITS)
-
/*
* A table of GC handle data, implementing a simple lock-free bitmap allocator.
*
- * 'entries' is an array of pointers to buckets of increasing size. The first
- * bucket has size 'MIN_BUCKET_SIZE', and each bucket is twice the size of the
- * previous, i.e.:
- *
- * |-------|-- MIN_BUCKET_SIZE
- * [0] -> xxxxxxxx
- * [1] -> xxxxxxxxxxxxxxxx
- * [2] -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- * ...
- *
- * The size of the spine, 'BUCKETS', is chosen so that the maximum number of
- * entries is no less than the maximum index value of a GC handle.
- *
* Each entry in a bucket is a pointer with two tag bits: if
* 'GC_HANDLE_OCCUPIED' returns true for a slot, then the slot is occupied; if
* so, then 'GC_HANDLE_VALID' gives whether the entry refers to a valid (1) or
* object pointer. If the reference is NULL, and 'GC_HANDLE_TYPE_IS_WEAK' is
* true for 'type', then the pointer is a metadata pointer--this allows us to
* retrieve the domain ID of an expired weak reference in Mono.
- *
- * Finally, 'slot_hint' denotes the position of the last allocation, so that the
- * whole array needn't be searched on every allocation.
*/
typedef struct {
- volatile gpointer *volatile entries [BUCKETS];
- volatile guint32 capacity;
- volatile guint32 slot_hint;
- volatile guint32 max_index;
+ SgenArrayList entries_array;
guint8 type;
} HandleData;
-static inline guint
-bucket_size (guint index)
-{
- return 1 << (index + MIN_BUCKET_BITS);
-}
-
-/* Computes floor(log2(index + MIN_BUCKET_SIZE)) - 1, giving the index
- * of the bucket containing a slot.
- */
-static inline guint
-index_bucket (guint index)
-{
-#ifdef __GNUC__
- return CHAR_BIT * sizeof (index) - __builtin_clz (index + MIN_BUCKET_SIZE) - 1 - MIN_BUCKET_BITS;
-#else
- guint count = 0;
- index += MIN_BUCKET_SIZE;
- while (index) {
- ++count;
- index >>= 1;
- }
- return count - 1 - MIN_BUCKET_BITS;
-#endif
-}
-
-static inline void
-bucketize (guint index, guint *bucket, guint *offset)
-{
- *bucket = index_bucket (index);
- *offset = index - bucket_size (*bucket) + MIN_BUCKET_SIZE;
-}
-
static void
protocol_gchandle_update (int handle_type, gpointer link, gpointer old_value, gpointer new_value)
{
return NULL;
}
+static inline gboolean
+is_slot_set (volatile gpointer *slot)
+{
+ gpointer entry = *slot;
+ if (MONO_GC_HANDLE_OCCUPIED (entry))
+ return TRUE;
+ return FALSE;
+}
+
/* Try to claim a slot by setting its occupied bit. */
static inline gboolean
-try_occupy_slot (HandleData *handles, guint bucket, guint offset, GCObject *obj, gboolean track)
+try_occupy_slot (volatile gpointer *slot, gpointer obj, int data)
{
- volatile gpointer *link_addr = &(handles->entries [bucket] [offset]);
- if (MONO_GC_HANDLE_OCCUPIED (*link_addr))
+ if (is_slot_set (slot))
return FALSE;
- return try_set_slot (link_addr, obj, NULL, (GCHandleType)handles->type) != NULL;
+ return try_set_slot (slot, (GCObject *)obj, NULL, (GCHandleType)data) != NULL;
+}
+
+static void
+bucket_alloc_callback (gpointer *bucket, guint32 new_bucket_size, gboolean alloc)
+{
+ if (alloc)
+ sgen_register_root ((char *)bucket, new_bucket_size, SGEN_DESCRIPTOR_NULL, ROOT_TYPE_PINNED, MONO_ROOT_SOURCE_GC_HANDLE, "pinned gc handles");
+ else
+ sgen_deregister_root ((char *)bucket);
}
static HandleData gc_handles [] = {
- { { NULL }, 0, 0, 0, (HANDLE_WEAK) },
- { { NULL }, 0, 0, 0, (HANDLE_WEAK_TRACK) },
- { { NULL }, 0, 0, 0, (HANDLE_NORMAL) },
- { { NULL }, 0, 0, 0, (HANDLE_PINNED) }
+ { SGEN_ARRAY_LIST_INIT (NULL, is_slot_set, try_occupy_slot, -1), (HANDLE_WEAK) },
+ { SGEN_ARRAY_LIST_INIT (NULL, is_slot_set, try_occupy_slot, -1), (HANDLE_WEAK_TRACK) },
+ { SGEN_ARRAY_LIST_INIT (NULL, is_slot_set, try_occupy_slot, -1), (HANDLE_NORMAL) },
+ { SGEN_ARRAY_LIST_INIT (bucket_alloc_callback, is_slot_set, try_occupy_slot, -1), (HANDLE_PINNED) }
};
static HandleData *
sgen_mark_normal_gc_handles (void *addr, SgenUserMarkFunc mark_func, void *gc_data)
{
HandleData *handles = gc_handles_for_type (HANDLE_NORMAL);
- size_t bucket, offset;
- const guint max_bucket = index_bucket (handles->capacity);
- guint32 index = 0;
- const guint32 max_index = handles->max_index;
- for (bucket = 0; bucket < max_bucket; ++bucket) {
- volatile gpointer *entries = handles->entries [bucket];
- for (offset = 0; offset < bucket_size (bucket); ++offset, ++index) {
- volatile gpointer *entry;
- gpointer hidden, revealed;
- /* No need to iterate beyond the largest index ever allocated. */
- if (index > max_index)
- return;
- entry = &entries [offset];
- hidden = *entry;
- revealed = MONO_GC_REVEAL_POINTER (hidden, FALSE);
- if (!MONO_GC_HANDLE_IS_OBJECT_POINTER (hidden))
- continue;
- mark_func ((MonoObject **)&revealed, gc_data);
- g_assert (revealed);
- *entry = MONO_GC_HANDLE_OBJECT_POINTER (revealed, FALSE);
- }
- }
+ SgenArrayList *array = &handles->entries_array;
+ volatile gpointer *slot;
+ gpointer hidden, revealed;
+
+ SGEN_ARRAY_LIST_FOREACH_SLOT (array, slot) {
+ hidden = *slot;
+ revealed = MONO_GC_REVEAL_POINTER (hidden, FALSE);
+ if (!MONO_GC_HANDLE_IS_OBJECT_POINTER (hidden))
+ continue;
+ mark_func ((MonoObject **)&revealed, gc_data);
+ g_assert (revealed);
+ *slot = MONO_GC_HANDLE_OBJECT_POINTER (revealed, FALSE);
+ } SGEN_ARRAY_LIST_END_FOREACH_SLOT;
}
-static guint
-handle_data_find_unset (HandleData *handles, guint32 begin, guint32 end)
-{
- guint index;
- gint delta = begin < end ? +1 : -1;
- for (index = begin; index < end; index += delta) {
- guint bucket, offset;
- volatile gpointer *entries;
- bucketize (index, &bucket, &offset);
- entries = handles->entries [bucket];
- g_assert (entries);
- if (!MONO_GC_HANDLE_OCCUPIED (entries [offset]))
- return index;
- }
- return -1;
-}
-
-/* Adds a bucket if necessary and possible. */
-static void
-handle_data_grow (HandleData *handles, guint32 old_capacity)
-{
- const guint new_bucket = index_bucket (old_capacity);
- const guint32 growth = bucket_size (new_bucket);
- const guint32 new_capacity = old_capacity + growth;
- gpointer *entries;
- const size_t new_bucket_size = sizeof (**handles->entries) * growth;
- if (handles->capacity >= new_capacity)
- return;
- entries = (gpointer *)g_malloc0 (new_bucket_size);
- if (handles->type == HANDLE_PINNED)
- sgen_register_root ((char *)entries, new_bucket_size, SGEN_DESCRIPTOR_NULL, ROOT_TYPE_PINNED, MONO_ROOT_SOURCE_GC_HANDLE, "pinned gc handles");
- /* The zeroing of the newly allocated bucket must be complete before storing
- * the new bucket pointer.
- */
- mono_memory_write_barrier ();
- if (InterlockedCompareExchangePointer ((volatile gpointer *)&handles->entries [new_bucket], entries, NULL) == NULL) {
- /* It must not be the case that we succeeded in setting the bucket
- * pointer, while someone else succeeded in changing the capacity.
- */
- if (InterlockedCompareExchange ((volatile gint32 *)&handles->capacity, new_capacity, old_capacity) != old_capacity)
- g_assert_not_reached ();
- handles->slot_hint = old_capacity;
- return;
- }
- /* Someone beat us to the allocation. */
- if (handles->type == HANDLE_PINNED)
- sgen_deregister_root ((char *)entries);
- g_free (entries);
-}
static guint32
alloc_handle (HandleData *handles, GCObject *obj, gboolean track)
{
- guint index;
- guint32 res;
- guint bucket, offset;
- guint32 capacity;
- guint32 slot_hint;
- guint32 max_index;
- if (!handles->capacity)
- handle_data_grow (handles, 0);
-retry:
- capacity = handles->capacity;
- slot_hint = handles->slot_hint;
- index = handle_data_find_unset (handles, slot_hint, capacity);
- if (index == -1)
- index = handle_data_find_unset (handles, 0, slot_hint);
- if (index == -1) {
- handle_data_grow (handles, capacity);
- goto retry;
- }
- handles->slot_hint = index;
+ guint32 res, index;
+ SgenArrayList *array = &handles->entries_array;
/*
* If a GC happens shortly after a new bucket is allocated, the entire
* we track the maximum index seen so far, so that we can skip the empty
* slots.
*
- * Note that we update `max_index` before we even try occupying the
+ * Note that we update `next_slot` before we even try occupying the
* slot. If we did it the other way around and a GC happened in
* between, the GC wouldn't know that the slot was occupied. This is
* not a huge deal since `obj` is on the stack and thus pinned anyway,
* but hopefully some day it won't be anymore.
*/
- do {
- max_index = handles->max_index;
- if (index <= max_index)
- break;
- } while (InterlockedCompareExchange ((volatile gint32 *)&handles->max_index, index, max_index) != max_index);
-
- bucketize (index, &bucket, &offset);
- if (!try_occupy_slot (handles, bucket, offset, obj, track))
- goto retry;
+ index = sgen_array_list_add (array, obj, handles->type, TRUE);
#ifdef HEAVY_STATISTICS
InterlockedIncrement ((volatile gint32 *)&stat_gc_handles_allocated);
if (stat_gc_handles_allocated > stat_gc_handles_max_allocated)
sgen_gchandle_iterate (GCHandleType handle_type, int max_generation, SgenGCHandleIterateCallback callback, gpointer user)
{
HandleData *handle_data = gc_handles_for_type (handle_type);
- size_t bucket, offset;
- guint max_bucket = index_bucket (handle_data->capacity);
- guint32 index = 0;
- guint32 max_index = handle_data->max_index;
+ SgenArrayList *array = &handle_data->entries_array;
+ gpointer hidden, result, occupied;
+ volatile gpointer *slot;
+
/* If a new bucket has been allocated, but the capacity has not yet been
* increased, nothing can yet have been allocated in the bucket because the
* world is stopped, so we shouldn't miss any handles during iteration.
*/
- for (bucket = 0; bucket < max_bucket; ++bucket) {
- volatile gpointer *entries = handle_data->entries [bucket];
- for (offset = 0; offset < bucket_size (bucket); ++offset, ++index) {
- gpointer hidden;
- gpointer result;
- /* Table must contain no garbage pointers. */
- gboolean occupied;
- /* No need to iterate beyond the largest index ever allocated. */
- if (index > max_index)
- return;
- hidden = entries [offset];
- occupied = MONO_GC_HANDLE_OCCUPIED (hidden);
- g_assert (hidden ? occupied : !occupied);
- if (!occupied)
- continue;
- result = callback (hidden, handle_type, max_generation, user);
- if (result)
- SGEN_ASSERT (0, MONO_GC_HANDLE_OCCUPIED (result), "Why did the callback return an unoccupied entry?");
- else
- HEAVY_STAT (InterlockedDecrement ((volatile gint32 *)&stat_gc_handles_allocated));
- protocol_gchandle_update (handle_type, (gpointer)&entries [offset], hidden, result);
- entries [offset] = result;
- }
- }
+ SGEN_ARRAY_LIST_FOREACH_SLOT (array, slot) {
+ hidden = *slot;
+ occupied = (gpointer) MONO_GC_HANDLE_OCCUPIED (hidden);
+ g_assert (hidden ? !!occupied : !occupied);
+ if (!occupied)
+ continue;
+ result = callback (hidden, handle_type, max_generation, user);
+ if (result)
+ SGEN_ASSERT (0, MONO_GC_HANDLE_OCCUPIED (result), "Why did the callback return an unoccupied entry?");
+ else
+ HEAVY_STAT (InterlockedDecrement ((volatile gint32 *)&stat_gc_handles_allocated));
+ protocol_gchandle_update (handle_type, (gpointer)slot, hidden, result);
+ *slot = result;
+ } SGEN_ARRAY_LIST_END_FOREACH_SLOT;
}
/**
/* Invalid handles are possible; accessing one should produce NULL. (#34276) */
if (!handles)
return NULL;
- guint bucket, offset;
- g_assert (index < handles->capacity);
- bucketize (index, &bucket, &offset);
- return link_get (&handles->entries [bucket] [offset], MONO_GC_HANDLE_TYPE_IS_WEAK (type));
+ return link_get (sgen_array_list_get_slot (&handles->entries_array, index), MONO_GC_HANDLE_TYPE_IS_WEAK (type));
}
void
sgen_gchandle_set_target (guint32 gchandle, GCObject *obj)
{
- guint index = MONO_GC_HANDLE_SLOT (gchandle);
+ guint32 index = MONO_GC_HANDLE_SLOT (gchandle);
GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
HandleData *handles = gc_handles_for_type (type);
+ volatile gpointer *slot;
+ gpointer entry;
+
if (!handles)
return;
- guint bucket, offset;
- gpointer slot;
- g_assert (index < handles->capacity);
- bucketize (index, &bucket, &offset);
+ slot = sgen_array_list_get_slot (&handles->entries_array, index);
do {
- slot = handles->entries [bucket] [offset];
- SGEN_ASSERT (0, MONO_GC_HANDLE_OCCUPIED (slot), "Why are we setting the target on an unoccupied slot?");
- } while (!try_set_slot (&handles->entries [bucket] [offset], obj, slot, (GCHandleType)handles->type));
+ entry = *slot;
+ SGEN_ASSERT (0, MONO_GC_HANDLE_OCCUPIED (entry), "Why are we setting the target on an unoccupied slot?");
+ } while (!try_set_slot (slot, obj, entry, (GCHandleType)handles->type));
}
static gpointer
-mono_gchandle_slot_metadata (volatile gpointer *slot_addr, gboolean is_weak)
+mono_gchandle_slot_metadata (volatile gpointer *slot, gboolean is_weak)
{
- gpointer slot;
+ gpointer entry;
gpointer metadata;
retry:
- slot = *slot_addr;
- if (!MONO_GC_HANDLE_OCCUPIED (slot))
+ entry = *slot;
+ if (!MONO_GC_HANDLE_OCCUPIED (entry))
return NULL;
- if (MONO_GC_HANDLE_IS_OBJECT_POINTER (slot)) {
- GCObject *obj = (GCObject *)MONO_GC_REVEAL_POINTER (slot, is_weak);
+ if (MONO_GC_HANDLE_IS_OBJECT_POINTER (entry)) {
+ GCObject *obj = (GCObject *)MONO_GC_REVEAL_POINTER (entry, is_weak);
/* See note [dummy use]. */
sgen_dummy_use (obj);
/*
* at this point and recompute it later, in which case we would still use
* it.
*/
- if (*slot_addr != slot)
+ if (*slot != entry)
goto retry;
return sgen_client_metadata_for_object (obj);
}
- metadata = MONO_GC_REVEAL_POINTER (slot, is_weak);
+ metadata = MONO_GC_REVEAL_POINTER (entry, is_weak);
/* See note [dummy use]. */
sgen_dummy_use (metadata);
- if (*slot_addr != slot)
+ if (*slot != entry)
goto retry;
return metadata;
}
gpointer
sgen_gchandle_get_metadata (guint32 gchandle)
{
- guint index = MONO_GC_HANDLE_SLOT (gchandle);
+ guint32 index = MONO_GC_HANDLE_SLOT (gchandle);
GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
HandleData *handles = gc_handles_for_type (type);
+ volatile gpointer *slot;
+
if (!handles)
return NULL;
- guint bucket, offset;
- if (index >= handles->capacity)
+ if (index >= handles->entries_array.capacity)
return NULL;
- bucketize (index, &bucket, &offset);
- return mono_gchandle_slot_metadata (&handles->entries [bucket] [offset], MONO_GC_HANDLE_TYPE_IS_WEAK (type));
+
+ slot = sgen_array_list_get_slot (&handles->entries_array, index);
+
+ return mono_gchandle_slot_metadata (slot, MONO_GC_HANDLE_TYPE_IS_WEAK (type));
}
/**
void
mono_gchandle_free (guint32 gchandle)
{
- guint index = MONO_GC_HANDLE_SLOT (gchandle);
+ guint32 index = MONO_GC_HANDLE_SLOT (gchandle);
GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
HandleData *handles = gc_handles_for_type (type);
+ volatile gpointer *slot;
+ gpointer entry;
if (!handles)
return;
- guint bucket, offset;
- gpointer slot;
- bucketize (index, &bucket, &offset);
- slot = handles->entries [bucket] [offset];
- if (index < handles->capacity && MONO_GC_HANDLE_OCCUPIED (slot)) {
- handles->entries [bucket] [offset] = NULL;
- protocol_gchandle_update (handles->type, (gpointer)&handles->entries [bucket] [offset], slot, NULL);
+
+ slot = sgen_array_list_get_slot (&handles->entries_array, index);
+ entry = *slot;
+
+ if (index < handles->entries_array.capacity && MONO_GC_HANDLE_OCCUPIED (entry)) {
+ *slot = NULL;
+ protocol_gchandle_update (handles->type, (gpointer)slot, entry, NULL);
HEAVY_STAT (InterlockedDecrement ((volatile gint32 *)&stat_gc_handles_allocated));
} else {
/* print a warning? */
unsigned char *free_chunk_map;
};
+/* We allow read only access on the list while sweep is not running */
LOSObject *los_object_list = NULL;
mword los_memory_usage = 0;
*vtslot = vtable;
sgen_update_heap_boundaries ((mword)obj->data, (mword)obj->data + size);
obj->next = los_object_list;
+ /*
+ * We need a memory barrier so we don't expose as head of the los object list
+ * a LOSObject that doesn't have its fields initialized.
+ */
+ mono_memory_write_barrier ();
los_object_list = obj;
los_memory_usage += size;
los_num_objects++;
}
void
-sgen_los_scan_card_table (gboolean mod_union, ScanCopyContext ctx)
+sgen_los_scan_card_table (CardTableScanType scan_type, ScanCopyContext ctx)
{
LOSObject *obj;
- binary_protocol_los_card_table_scan_start (sgen_timestamp (), mod_union);
+ binary_protocol_los_card_table_scan_start (sgen_timestamp (), scan_type & CARDTABLE_SCAN_MOD_UNION);
for (obj = los_object_list; obj; obj = obj->next) {
+ mword num_cards = 0;
guint8 *cards;
if (!SGEN_OBJECT_HAS_REFERENCES (obj->data))
continue;
- if (mod_union) {
+ if (scan_type & CARDTABLE_SCAN_MOD_UNION) {
if (!sgen_los_object_is_pinned (obj->data))
continue;
cards = get_cardtable_mod_union_for_object (obj);
g_assert (cards);
+ if (scan_type == CARDTABLE_SCAN_MOD_UNION_PRECLEAN) {
+ guint8 *cards_preclean;
+ mword obj_size = sgen_los_object_size (obj);
+ num_cards = sgen_card_table_number_of_cards_in_range ((mword) obj->data, obj_size);
+ cards_preclean = (guint8 *)sgen_alloc_internal_dynamic (num_cards, INTERNAL_MEM_CARDTABLE_MOD_UNION, TRUE);
+
+ sgen_card_table_preclean_mod_union (cards, cards_preclean, num_cards);
+
+ cards = cards_preclean;
+ }
} else {
cards = NULL;
}
- sgen_cardtable_scan_object (obj->data, sgen_los_object_size (obj), cards, mod_union, ctx);
+ sgen_cardtable_scan_object (obj->data, sgen_los_object_size (obj), cards, ctx);
+
+ if (scan_type == CARDTABLE_SCAN_MOD_UNION_PRECLEAN)
+ sgen_free_internal_dynamic (cards, num_cards, INTERNAL_MEM_CARDTABLE_MOD_UNION);
}
- binary_protocol_los_card_table_scan_end (sgen_timestamp (), mod_union);
+ binary_protocol_los_card_table_scan_end (sgen_timestamp (), scan_type & CARDTABLE_SCAN_MOD_UNION);
}
void
#include "sgen-scan-object.h"
}
+#ifdef SCAN_VTYPE_FUNCTION_NAME
+static void
+SCAN_VTYPE_FUNCTION_NAME (GCObject *full_object, char *start, SgenDescriptor desc, SgenGrayQueue *queue BINARY_PROTOCOL_ARG (size_t size))
+{
+ SGEN_OBJECT_LAYOUT_STATISTICS_DECLARE_BITMAP;
+
+#ifdef HEAVY_STATISTICS
+ /* FIXME: We're half scanning this object. How do we account for that? */
+ //add_scanned_object (start);
+#endif
+
+ /* The descriptors include info about the object header as well */
+ start -= SGEN_CLIENT_OBJECT_HEADER_SIZE;
+
+ /* We use the same HANDLE_PTR from the obj scan function */
+#define SCAN_OBJECT_NOVTABLE
+#define SCAN_OBJECT_PROTOCOL
+#include "sgen-scan-object.h"
+
+ SGEN_OBJECT_LAYOUT_STATISTICS_COMMIT_BITMAP;
+}
+#endif
+
+#ifdef SCAN_PTR_FIELD_FUNCTION_NAME
+static void
+SCAN_PTR_FIELD_FUNCTION_NAME (GCObject *full_object, GCObject **ptr, SgenGrayQueue *queue)
+{
+ HANDLE_PTR (ptr, NULL);
+}
+#endif
+
static gboolean
DRAIN_GRAY_STACK_FUNCTION_NAME (SgenGrayQueue *queue)
{
#undef COPY_OR_MARK_FUNCTION_NAME
#undef COPY_OR_MARK_WITH_EVACUATION
+#undef COPY_OR_MARK_CONCURRENT
+#undef COPY_OR_MARK_CONCURRENT_WITH_EVACUATION
#undef SCAN_OBJECT_FUNCTION_NAME
+#undef SCAN_VTYPE_FUNCTION_NAME
+#undef SCAN_PTR_FIELD_FUNCTION_NAME
#undef DRAIN_GRAY_STACK_FUNCTION_NAME
+++ /dev/null
-/*
- * sgen-major-scan-object.h: Object scanning in the major collectors.
- *
- * Copyright 2001-2003 Ximian, Inc
- * Copyright 2003-2010 Novell, Inc.
- * Copyright (C) 2012 Xamarin Inc
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License 2.0 as published by the Free Software Foundation;
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License 2.0 along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/*
- * FIXME: We use the same scanning function in the concurrent collector whether we scan
- * during the starting/finishing collection pause (with the world stopped) or from the
- * concurrent worker thread.
- *
- * As long as the world is stopped, we should just follow pointers into the nursery and
- * evict if possible. In that case we also don't need the ALWAYS_ADD_TO_GLOBAL_REMSET case,
- * which only seems to make sense for when the world is stopped, in which case we only need
- * it because we don't follow into the nursery.
- */
-
-#undef HANDLE_PTR
-#define HANDLE_PTR(ptr,obj) do { \
- GCObject *__old = *(ptr); \
- binary_protocol_scan_process_reference ((full_object), (ptr), __old); \
- if (__old) { \
- gboolean __still_in_nursery = major_copy_or_mark_object_with_evacuation ((ptr), __old, queue); \
- if (G_UNLIKELY (__still_in_nursery && !sgen_ptr_in_nursery ((ptr)) && !SGEN_OBJECT_IS_CEMENTED (*(ptr)))) { \
- GCObject *__copy = *(ptr); \
- sgen_add_to_global_remset ((ptr), __copy); \
- } \
- } \
- } while (0)
-
-
-static void
-major_scan_vtype_concurrent_finish (GCObject *full_object, char *start, SgenDescriptor desc, SgenGrayQueue *queue BINARY_PROTOCOL_ARG (size_t size))
-{
- SGEN_OBJECT_LAYOUT_STATISTICS_DECLARE_BITMAP;
-
-#ifdef HEAVY_STATISTICS
- /* FIXME: We're half scanning this object. How do we account for that? */
- //add_scanned_object (start);
-#endif
-
- /* The descriptors include info about the object header as well */
- start -= SGEN_CLIENT_OBJECT_HEADER_SIZE;
-
-#define SCAN_OBJECT_NOVTABLE
-#define SCAN_OBJECT_PROTOCOL
-#include "sgen-scan-object.h"
-
- SGEN_OBJECT_LAYOUT_STATISTICS_COMMIT_BITMAP;
-}
#include "mono/sgen/sgen-memory-governor.h"
#include "mono/sgen/sgen-layout-stats.h"
#include "mono/sgen/sgen-pointer-queue.h"
+#include "mono/sgen/sgen-array-list.h"
#include "mono/sgen/sgen-pinning.h"
#include "mono/sgen/sgen-workers.h"
#include "mono/sgen/sgen-thread-pool.h"
#include "mono/sgen/sgen-client.h"
-#include "mono/utils/mono-membar.h"
+#include "mono/utils/mono-memory-model.h"
#if defined(ARCH_MIN_MS_BLOCK_SIZE) && defined(ARCH_MIN_MS_BLOCK_SIZE_SHIFT)
#define MS_BLOCK_SIZE ARCH_MIN_MS_BLOCK_SIZE
#define BLOCK_TAG(bl) ((bl)->has_references ? BLOCK_TAG_HAS_REFERENCES ((bl)) : (bl))
/* all allocated blocks in the system */
-static SgenPointerQueue allocated_blocks;
+static SgenArrayList allocated_blocks = SGEN_ARRAY_LIST_INIT (NULL, NULL, NULL, INTERNAL_MEM_PIN_QUEUE);
/* non-allocated block free-list */
static void *empty_blocks = NULL;
static size_t num_empty_blocks = 0;
-#define FOREACH_BLOCK_NO_LOCK_CONDITION(cond,bl) { \
- size_t __index; \
- SGEN_ASSERT (0, (cond) && !sweep_in_progress (), "Can't iterate blocks while the world is running or sweep is in progress."); \
- for (__index = 0; __index < allocated_blocks.next_slot; ++__index) { \
- (bl) = BLOCK_UNTAG (allocated_blocks.data [__index]);
-#define FOREACH_BLOCK_NO_LOCK(bl) \
- FOREACH_BLOCK_NO_LOCK_CONDITION(sgen_is_world_stopped (), bl)
+#define FOREACH_BLOCK_NO_LOCK(bl) { \
+ volatile gpointer *slot; \
+ SGEN_ASSERT (0, !sweep_in_progress (), "Can't iterate blocks while sweep is in progress."); \
+ SGEN_ARRAY_LIST_FOREACH_SLOT (&allocated_blocks, slot) { \
+ (bl) = BLOCK_UNTAG (*slot);
#define FOREACH_BLOCK_HAS_REFERENCES_NO_LOCK(bl,hr) { \
- size_t __index; \
- SGEN_ASSERT (0, sgen_is_world_stopped () && !sweep_in_progress (), "Can't iterate blocks while the world is running or sweep is in progress."); \
- for (__index = 0; __index < allocated_blocks.next_slot; ++__index) { \
- (bl) = (MSBlockInfo *)allocated_blocks.data [__index]; \
+ volatile gpointer *slot; \
+ SGEN_ASSERT (0, !sweep_in_progress (), "Can't iterate blocks while sweep is in progress."); \
+ SGEN_ARRAY_LIST_FOREACH_SLOT (&allocated_blocks, slot) { \
+ (bl) = (MSBlockInfo *) (*slot); \
(hr) = BLOCK_IS_TAGGED_HAS_REFERENCES ((bl)); \
(bl) = BLOCK_UNTAG ((bl));
-#define END_FOREACH_BLOCK_NO_LOCK } }
+#define END_FOREACH_BLOCK_NO_LOCK } SGEN_ARRAY_LIST_END_FOREACH_SLOT; }
static volatile size_t num_major_sections = 0;
/*
g_assert (block->free_list);
/* the block must be in the allocated_blocks array */
- g_assert (sgen_pointer_queue_find (&allocated_blocks, BLOCK_TAG (block)) != (size_t)-1);
+ g_assert (sgen_array_list_find (&allocated_blocks, BLOCK_TAG (block)) != (guint32)-1);
}
}
major_finish_sweep_checking ();
mono_memory_barrier ();
- sgen_pointer_queue_add (&allocated_blocks, BLOCK_TAG (info));
+ sgen_array_list_add (&allocated_blocks, BLOCK_TAG (info), 0, FALSE);
SGEN_ATOMIC_ADD_P (num_major_sections, 1);
return TRUE;
SGEN_ASSERT (0, success, "Could not set sweep state.");
}
-static gboolean ensure_block_is_checked_for_sweeping (int block_index, gboolean wait, gboolean *have_checked);
+static gboolean ensure_block_is_checked_for_sweeping (guint32 block_index, gboolean wait, gboolean *have_checked);
static SgenThreadPoolJob * volatile sweep_job;
static void
major_finish_sweep_checking (void)
{
- int block_index;
+ guint32 block_index;
SgenThreadPoolJob *job;
retry:
} else {
sgen_los_mark_mod_union_card (obj, ptr);
}
-
binary_protocol_mod_union_remset (obj, ptr, value_obj, SGEN_LOAD_VTABLE (value_obj));
}
#define COPY_OR_MARK_WITH_EVACUATION
#define COPY_OR_MARK_FUNCTION_NAME major_copy_or_mark_object_with_evacuation
#define SCAN_OBJECT_FUNCTION_NAME major_scan_object_with_evacuation
+#define SCAN_VTYPE_FUNCTION_NAME major_scan_vtype_with_evacuation
#define DRAIN_GRAY_STACK_FUNCTION_NAME drain_gray_stack_with_evacuation
+#define SCAN_PTR_FIELD_FUNCTION_NAME major_scan_ptr_field_with_evacuation
#include "sgen-marksweep-drain-gray-stack.h"
-#undef COPY_OR_MARK_WITH_EVACUATION
#define COPY_OR_MARK_CONCURRENT
#define COPY_OR_MARK_FUNCTION_NAME major_copy_or_mark_object_concurrent_no_evacuation
#define SCAN_OBJECT_FUNCTION_NAME major_scan_object_concurrent_no_evacuation
#define DRAIN_GRAY_STACK_FUNCTION_NAME drain_gray_stack_concurrent_no_evacuation
#include "sgen-marksweep-drain-gray-stack.h"
-#undef COPY_OR_MARK_CONCURRENT
#define COPY_OR_MARK_CONCURRENT_WITH_EVACUATION
#define COPY_OR_MARK_FUNCTION_NAME major_copy_or_mark_object_concurrent_with_evacuation
#define SCAN_OBJECT_FUNCTION_NAME major_scan_object_concurrent_with_evacuation
+#define SCAN_VTYPE_FUNCTION_NAME major_scan_vtype_concurrent_with_evacuation
+#define SCAN_PTR_FIELD_FUNCTION_NAME major_scan_ptr_field_concurrent_with_evacuation
#define DRAIN_GRAY_STACK_FUNCTION_NAME drain_gray_stack_concurrent_with_evacuation
#include "sgen-marksweep-drain-gray-stack.h"
return drain_gray_stack_concurrent_no_evacuation (queue);
}
-#include "sgen-marksweep-scan-object-concurrent.h"
-
static void
major_copy_or_mark_object_canonical (GCObject **ptr, SgenGrayQueue *queue)
{
* be correct, i.e. must not be used.
*/
static gboolean
-ensure_block_is_checked_for_sweeping (int block_index, gboolean wait, gboolean *have_checked)
+ensure_block_is_checked_for_sweeping (guint32 block_index, gboolean wait, gboolean *have_checked)
{
int count;
gboolean have_live = FALSE;
int i;
void *tagged_block;
MSBlockInfo *block;
+ volatile gpointer *block_slot = sgen_array_list_get_slot (&allocated_blocks, block_index);
SGEN_ASSERT (6, sweep_in_progress (), "Why do we call this function if there's no sweep in progress?");
*have_checked = FALSE;
retry:
- tagged_block = *(void * volatile *)&allocated_blocks.data [block_index];
+ tagged_block = *(void * volatile *)block_slot;
if (!tagged_block)
return FALSE;
goto retry;
}
- if (SGEN_CAS_PTR (&allocated_blocks.data [block_index], BLOCK_TAG_CHECKING (tagged_block), tagged_block) != tagged_block)
+ if (SGEN_CAS_PTR (block_slot, BLOCK_TAG_CHECKING (tagged_block), tagged_block) != tagged_block)
goto retry;
block = BLOCK_UNTAG (tagged_block);
* block list and freed.
*/
SGEN_ASSERT (6, block_index < allocated_blocks.next_slot, "How did the number of blocks shrink?");
- SGEN_ASSERT (6, allocated_blocks.data [block_index] == BLOCK_TAG_CHECKING (tagged_block), "How did the block move?");
+ SGEN_ASSERT (6, *block_slot == BLOCK_TAG_CHECKING (tagged_block), "How did the block move?");
binary_protocol_empty (MS_BLOCK_OBJ (block, 0), (char*)MS_BLOCK_OBJ (block, count) - (char*)MS_BLOCK_OBJ (block, 0));
ms_free_block (block);
}
done:
- allocated_blocks.data [block_index] = tagged_block;
+ *block_slot = tagged_block;
return !!tagged_block;
}
static void
sweep_job_func (void *thread_data_untyped, SgenThreadPoolJob *job)
{
- int block_index;
- int num_blocks = num_major_sections_before_sweep;
+ guint32 block_index;
+ guint32 num_blocks = num_major_sections_before_sweep;
SGEN_ASSERT (0, sweep_in_progress (), "Sweep thread called with wrong state");
SGEN_ASSERT (0, num_blocks <= allocated_blocks.next_slot, "How did we lose blocks?");
* cooperate with the sweep thread to finish sweeping, and they will traverse from
* low to high, to avoid constantly colliding on the same blocks.
*/
- for (block_index = num_blocks - 1; block_index >= 0; --block_index) {
- gboolean have_checked;
-
+ for (block_index = num_blocks; block_index-- > 0;) {
/*
* The block might have been freed by another thread doing some checking
* work.
*/
- if (!ensure_block_is_checked_for_sweeping (block_index, TRUE, &have_checked))
+ if (!ensure_block_is_checked_for_sweeping (block_index, TRUE, NULL))
++num_major_sections_freed_in_sweep;
}
if (SGEN_MAX_ASSERT_LEVEL >= 6) {
for (block_index = num_blocks; block_index < allocated_blocks.next_slot; ++block_index) {
- MSBlockInfo *block = BLOCK_UNTAG (allocated_blocks.data [block_index]);
+ MSBlockInfo *block = BLOCK_UNTAG (*sgen_array_list_get_slot (&allocated_blocks, block_index));
SGEN_ASSERT (6, block && block->state == BLOCK_STATE_SWEPT, "How did a new block to be swept get added while swept?");
}
}
- sgen_pointer_queue_remove_nulls (&allocated_blocks);
- mono_memory_barrier ();
+ sgen_array_list_remove_nulls (&allocated_blocks);
sweep_finish ();
*/
major_finish_sweep_checking ();
- FOREACH_BLOCK_NO_LOCK_CONDITION (TRUE, block) {
+ FOREACH_BLOCK_NO_LOCK (block) {
int count = MS_BLOCK_FREE / block->obj_size;
void **iter;
size += count * block->obj_size;
#define MS_OBJ_ALLOCED_FAST(o,b) (*(void**)(o) && (*(char**)(o) < (b) || *(char**)(o) >= (b) + MS_BLOCK_SIZE))
static void
-scan_card_table_for_block (MSBlockInfo *block, gboolean mod_union, ScanCopyContext ctx)
+scan_card_table_for_block (MSBlockInfo *block, CardTableScanType scan_type, ScanCopyContext ctx)
{
SgenGrayQueue *queue = ctx.queue;
ScanObjectFunc scan_func = ctx.ops->scan_object;
#ifndef SGEN_HAVE_OVERLAPPING_CARDS
guint8 cards_copy [CARDS_PER_BLOCK];
#endif
+ guint8 cards_preclean [CARDS_PER_BLOCK];
gboolean small_objects;
int block_obj_size;
char *block_start;
guint8 *card_data_end;
char *scan_front = NULL;
+ /* The concurrent mark doesn't enter evacuating blocks */
+ if (scan_type == CARDTABLE_SCAN_MOD_UNION_PRECLEAN && major_block_is_evacuating (block))
+ return;
+
block_obj_size = block->obj_size;
small_objects = block_obj_size < CARD_SIZE_IN_BYTES;
* Cards aliasing happens in powers of two, so as long as major blocks are aligned to their
* sizes, they won't overflow the cardtable overlap modulus.
*/
- if (mod_union) {
+ if (scan_type & CARDTABLE_SCAN_MOD_UNION) {
card_data = card_base = block->cardtable_mod_union;
/*
* This happens when the nursery collection that precedes finishing
*/
if (!card_data)
return;
+
+ if (scan_type == CARDTABLE_SCAN_MOD_UNION_PRECLEAN) {
+ sgen_card_table_preclean_mod_union (card_data, cards_preclean, CARDS_PER_BLOCK);
+ card_data = card_base = cards_preclean;
+ }
} else {
#ifdef SGEN_HAVE_OVERLAPPING_CARDS
card_data = card_base = sgen_card_table_get_card_scan_address ((mword)block_start);
if (obj < scan_front || !MS_OBJ_ALLOCED_FAST (obj, block_start))
goto next_object;
- if (mod_union) {
+ if (scan_type & CARDTABLE_SCAN_MOD_UNION) {
/* FIXME: do this more efficiently */
int w, b;
MS_CALC_MARK_BIT (w, b, obj);
scan_func (object, sgen_obj_get_descriptor (object), queue);
} else {
size_t offset = sgen_card_table_get_card_offset (obj, block_start);
- sgen_cardtable_scan_object (object, block_obj_size, card_base + offset, mod_union, ctx);
+ sgen_cardtable_scan_object (object, block_obj_size, card_base + offset, ctx);
}
next_object:
obj += block_obj_size;
}
static void
-major_scan_card_table (gboolean mod_union, ScanCopyContext ctx)
+major_scan_card_table (CardTableScanType scan_type, ScanCopyContext ctx)
{
MSBlockInfo *block;
gboolean has_references;
if (!concurrent_mark)
- g_assert (!mod_union);
+ g_assert (scan_type == CARDTABLE_SCAN_GLOBAL);
major_finish_sweep_checking ();
- binary_protocol_major_card_table_scan_start (sgen_timestamp (), mod_union);
+ binary_protocol_major_card_table_scan_start (sgen_timestamp (), scan_type & CARDTABLE_SCAN_MOD_UNION);
FOREACH_BLOCK_HAS_REFERENCES_NO_LOCK (block, has_references) {
#ifdef PREFETCH_CARDS
int prefetch_index = __index + 6;
if (prefetch_index < allocated_blocks.next_slot) {
- MSBlockInfo *prefetch_block = BLOCK_UNTAG (allocated_blocks.data [prefetch_index]);
- guint8 *prefetch_cards = sgen_card_table_get_card_scan_address ((mword)MS_BLOCK_FOR_BLOCK_INFO (prefetch_block));
+ MSBlockInfo *prefetch_block = BLOCK_UNTAG (*sgen_array_list_get_slot (&allocated_blocks, prefetch_index));
PREFETCH_READ (prefetch_block);
- PREFETCH_WRITE (prefetch_cards);
- PREFETCH_WRITE (prefetch_cards + 32);
+ if (scan_type == CARDTABLE_SCAN_GLOBAL) {
+ guint8 *prefetch_cards = sgen_card_table_get_card_scan_address ((mword)MS_BLOCK_FOR_BLOCK_INFO (prefetch_block));
+ PREFETCH_WRITE (prefetch_cards);
+ PREFETCH_WRITE (prefetch_cards + 32);
+ }
}
#endif
if (!has_references)
continue;
- scan_card_table_for_block (block, mod_union, ctx);
+ scan_card_table_for_block (block, scan_type, ctx);
} END_FOREACH_BLOCK_NO_LOCK;
- binary_protocol_major_card_table_scan_end (sgen_timestamp (), mod_union);
+ binary_protocol_major_card_table_scan_end (sgen_timestamp (), scan_type & CARDTABLE_SCAN_MOD_UNION);
}
static void
if (is_concurrent) {
collector->major_ops_concurrent_start.copy_or_mark_object = major_copy_or_mark_object_concurrent_canonical;
collector->major_ops_concurrent_start.scan_object = major_scan_object_concurrent_with_evacuation;
+ collector->major_ops_concurrent_start.scan_vtype = major_scan_vtype_concurrent_with_evacuation;
+ collector->major_ops_concurrent_start.scan_ptr_field = major_scan_ptr_field_concurrent_with_evacuation;
collector->major_ops_concurrent_start.drain_gray_stack = drain_gray_stack_concurrent;
collector->major_ops_concurrent_finish.copy_or_mark_object = major_copy_or_mark_object_concurrent_finish_canonical;
collector->major_ops_concurrent_finish.scan_object = major_scan_object_with_evacuation;
- collector->major_ops_concurrent_finish.scan_vtype = major_scan_vtype_concurrent_finish;
+ collector->major_ops_concurrent_finish.scan_vtype = major_scan_vtype_with_evacuation;
+ collector->major_ops_concurrent_finish.scan_ptr_field = major_scan_ptr_field_with_evacuation;
collector->major_ops_concurrent_finish.drain_gray_stack = drain_gray_stack;
}
#include "sgen-scan-object.h"
}
+static void
+SERIAL_SCAN_PTR_FIELD (GCObject *full_object, GCObject **ptr, SgenGrayQueue *queue)
+{
+ HANDLE_PTR (ptr, NULL);
+}
+
#define FILL_MINOR_COLLECTOR_SCAN_OBJECT(collector) do { \
(collector)->serial_ops.scan_object = SERIAL_SCAN_OBJECT; \
(collector)->serial_ops.scan_vtype = SERIAL_SCAN_VTYPE; \
+ (collector)->serial_ops.scan_ptr_field = SERIAL_SCAN_PTR_FIELD; \
} while (0)
static volatile State workers_state;
static SgenObjectOperations * volatile idle_func_object_ops;
+static SgenThreadPoolJob * volatile preclean_job;
static guint64 stat_workers_num_finished;
sgen_drain_gray_stack (ctx);
} else {
- worker_try_finish ();
+ SgenThreadPoolJob *job = preclean_job;
+ if (job) {
+ sgen_thread_pool_job_enqueue (job);
+ preclean_job = NULL;
+ } else {
+ worker_try_finish ();
+ }
}
}
void
sgen_workers_stop_all_workers (void)
{
+ preclean_job = NULL;
+ mono_memory_write_barrier ();
forced_stop = TRUE;
sgen_thread_pool_wait_for_all_jobs ();
}
void
-sgen_workers_start_all_workers (SgenObjectOperations *object_ops)
+sgen_workers_start_all_workers (SgenObjectOperations *object_ops, SgenThreadPoolJob *job)
{
forced_stop = FALSE;
idle_func_object_ops = object_ops;
+ preclean_job = job;
mono_memory_write_barrier ();
sgen_workers_ensure_awake ();
void sgen_workers_init (int num_workers);
void sgen_workers_stop_all_workers (void);
-void sgen_workers_start_all_workers (SgenObjectOperations *object_ops);
+void sgen_workers_start_all_workers (SgenObjectOperations *object_ops, SgenThreadPoolJob *finish_job);
void sgen_workers_ensure_awake (void);
void sgen_workers_init_distribute_gray_queue (void);
void sgen_workers_enqueue_job (SgenThreadPoolJob *job, gboolean enqueue);
public class Tests
{
public static int Main(string[] args) {
- return TestDriver.RunTests (typeof (Tests), args);
+ if (args.Length == 0)
+ return TestDriver.RunTests (typeof (Tests), new String[] { "-v" });
+ else
+ return TestDriver.RunTests (typeof (Tests), args);
}
public static int test_0_unload () {
return del (42);
}
+typedef char* (STDCALL *IcallDelegate) (const char *);
+LIBTEST_API int STDCALL
+mono_test_marshal_icall_delegate (IcallDelegate del)
+{
+ char *res = del ("ABC");
+ return strcmp (res, "ABC") == 0 ? 0 : 1;
+}
+
LIBTEST_API int STDCALL
mono_test_marshal_stringbuilder (char *s, int n)
{
[DllImport ("libtest", EntryPoint="mono_test_marshal_virtual_delegate")]
public static extern int mono_test_marshal_virtual_delegate (VirtualDelegate del);
+ [DllImport ("libtest", EntryPoint="mono_test_marshal_icall_delegate")]
+ public static extern int mono_test_marshal_icall_delegate (IcallDelegate del);
+
+ public delegate string IcallDelegate (IntPtr p);
+
public delegate int TestDelegate (int a, ref SimpleStruct ss, int b);
public delegate SimpleStruct SimpleDelegate2 (SimpleStruct ss);
return mono_test_marshal_virtual_delegate (b.get_del ());
}
+
+ public static int test_0_icall_delegate () {
+ var m = typeof (Marshal).GetMethod ("PtrToStringAnsi", new Type[] { typeof (IntPtr) });
+
+ return mono_test_marshal_icall_delegate ((IcallDelegate)Delegate.CreateDelegate (typeof (IcallDelegate), m));
+ }
}
*/
#include <config.h>
-#ifdef CHECKED_BUILD
+#ifdef ENABLE_CHECKED_BUILD
#include <mono/utils/checked-build.h>
#include <mono/utils/mono-threads.h>
#include <execinfo.h>
#endif
+// Selective-enable support
+
+// Returns true for check modes which are allowed by both the current DISABLE_ macros and the MONO_CHECK_MODE env var.
+// Argument may be a bitmask; if so, result is true if at least one specified mode is enabled.
+mono_bool
+mono_check_mode_enabled (MonoCheckMode query)
+{
+ static MonoCheckMode check_mode = MONO_CHECK_MODE_UNKNOWN;
+ if (G_UNLIKELY (check_mode == MONO_CHECK_MODE_UNKNOWN))
+ {
+ MonoCheckMode env_check_mode = MONO_CHECK_MODE_NONE;
+ const gchar *env_string = g_getenv ("MONO_CHECK_MODE");
+
+ if (env_string)
+ {
+ gchar **env_split = g_strsplit (env_string, ",", 0);
+ for (gchar **env_component = env_split; *env_component; env_component++)
+ {
+ mono_bool check_all = g_str_equal (*env_component, "all");
+#ifdef ENABLE_CHECKED_BUILD_GC
+ if (check_all || g_str_equal (*env_component, "gc"))
+ env_check_mode |= MONO_CHECK_MODE_GC;
+#endif
+#ifdef ENABLE_CHECKED_BUILD_METADATA
+ if (check_all || g_str_equal (*env_component, "metadata"))
+ env_check_mode |= MONO_CHECK_MODE_METADATA;
+#endif
+#ifdef ENABLE_CHECKED_BUILD_THREAD
+ if (check_all || g_str_equal (*env_component, "thread"))
+ env_check_mode |= MONO_CHECK_MODE_THREAD;
+#endif
+ }
+ g_strfreev (env_split);
+ }
+
+ check_mode = env_check_mode;
+ }
+ return check_mode & query;
+}
+
+static int
+mono_check_transition_limit (void)
+{
+ static int transition_limit = -1;
+ if (transition_limit < 0) {
+ const gchar *env_string = g_getenv ("MONO_CHECK_THREAD_TRANSITION_HISTORY");
+ if (env_string)
+ transition_limit = atoi (env_string);
+ else
+ transition_limit = 3;
+ }
+ return transition_limit;
+}
+
typedef struct {
GPtrArray *transitions;
guint32 in_gc_critical_region;
void
checked_build_init (void)
{
- mono_native_tls_alloc (&thread_status, NULL);
+ // Init state for get_state, which can be called either by gc or thread mode
+ if (mono_check_mode_enabled (MONO_CHECK_MODE_GC | MONO_CHECK_MODE_THREAD))
+ mono_native_tls_alloc (&thread_status, NULL);
}
static CheckState*
return state;
}
-#if !defined(DISABLE_CHECKED_BUILD_THREAD)
+#ifdef ENABLE_CHECKED_BUILD_THREAD
#define MAX_NATIVE_BT 6
#define MAX_NATIVE_BT_PROBE (MAX_NATIVE_BT + 5)
-#define MAX_TRANSITIONS 3
+#define MAX_TRANSITIONS (mono_check_transition_limit ())
#ifdef HAVE_BACKTRACE_SYMBOLS
void
checked_build_thread_transition (const char *transition, void *info, int from_state, int suspend_count, int next_state, int suspend_count_delta)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_THREAD))
+ return;
+
MonoThreadInfo *cur = mono_thread_info_current_unchecked ();
CheckState *state = get_state ();
/* We currently don't record external changes as those are hard to reason about. */
g_ptr_array_add (state->transitions, t);
}
-#endif /* !defined(DISABLE_CHECKED_BUILD_THREAD) */
-
-#if !defined(DISABLE_CHECKED_BUILD_GC)
-
-static void
-assertion_fail (const char *msg, ...)
+void
+mono_fatal_with_history (const char *msg, ...)
{
int i;
GString* err = g_string_sized_new (100);
- CheckState *state = get_state ();
g_string_append_printf (err, "Assertion failure in thread %p due to: ", mono_native_thread_id_get ());
g_string_append_vprintf (err, msg, args);
va_end (args);
- g_string_append_printf (err, "\nLast %d state transitions: (most recent first)\n", state->transitions->len);
-
- for (i = state->transitions->len - 1; i >= 0; --i) {
- ThreadTransition *t = state->transitions->pdata [i];
- char *bt = translate_backtrace (t->backtrace, t->size);
- g_string_append_printf (err, "[%s] %s -> %s (%d) %s%d at:\n%s",
- t->name,
- mono_thread_state_name (t->from_state),
- mono_thread_state_name (t->next_state),
- t->suspend_count,
- t->suspend_count_delta > 0 ? "+" : "", //I'd like to see this sort of values: -1, 0, +1
- t->suspend_count_delta,
- bt);
- g_free (bt);
+ if (mono_check_mode_enabled (MONO_CHECK_MODE_THREAD))
+ {
+ CheckState *state = get_state ();
+
+ g_string_append_printf (err, "\nLast %d state transitions: (most recent first)\n", state->transitions->len);
+
+ for (i = state->transitions->len - 1; i >= 0; --i) {
+ ThreadTransition *t = state->transitions->pdata [i];
+ char *bt = translate_backtrace (t->backtrace, t->size);
+ g_string_append_printf (err, "[%s] %s -> %s (%d) %s%d at:\n%s",
+ t->name,
+ mono_thread_state_name (t->from_state),
+ mono_thread_state_name (t->next_state),
+ t->suspend_count,
+ t->suspend_count_delta > 0 ? "+" : "", //I'd like to see this sort of values: -1, 0, +1
+ t->suspend_count_delta,
+ bt);
+ g_free (bt);
+ }
}
g_error (err->str);
g_string_free (err, TRUE);
}
+#endif /* defined(ENABLE_CHECKED_BUILD_THREAD) */
+
+#ifdef ENABLE_CHECKED_BUILD_GC
+
void
assert_gc_safe_mode (void)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ return;
+
MonoThreadInfo *cur = mono_thread_info_current ();
int state;
if (!cur)
- assertion_fail ("Expected GC Safe mode but thread is not attached");
+ mono_fatal_with_history ("Expected GC Safe mode but thread is not attached");
switch (state = mono_thread_info_current_state (cur)) {
case STATE_BLOCKING:
case STATE_BLOCKING_AND_SUSPENDED:
break;
default:
- assertion_fail ("Expected GC Safe mode but was in %s state", mono_thread_state_name (state));
+ mono_fatal_with_history ("Expected GC Safe mode but was in %s state", mono_thread_state_name (state));
}
}
void
assert_gc_unsafe_mode (void)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ return;
+
MonoThreadInfo *cur = mono_thread_info_current ();
int state;
if (!cur)
- assertion_fail ("Expected GC Unsafe mode but thread is not attached");
+ mono_fatal_with_history ("Expected GC Unsafe mode but thread is not attached");
switch (state = mono_thread_info_current_state (cur)) {
case STATE_RUNNING:
case STATE_SELF_SUSPEND_REQUESTED:
break;
default:
- assertion_fail ("Expected GC Unsafe mode but was in %s state", mono_thread_state_name (state));
+ mono_fatal_with_history ("Expected GC Unsafe mode but was in %s state", mono_thread_state_name (state));
}
}
void
assert_gc_neutral_mode (void)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ return;
+
MonoThreadInfo *cur = mono_thread_info_current ();
int state;
if (!cur)
- assertion_fail ("Expected GC Neutral mode but thread is not attached");
+ mono_fatal_with_history ("Expected GC Neutral mode but thread is not attached");
switch (state = mono_thread_info_current_state (cur)) {
case STATE_RUNNING:
case STATE_BLOCKING_AND_SUSPENDED:
break;
default:
- assertion_fail ("Expected GC Neutral mode but was in %s state", mono_thread_state_name (state));
+ mono_fatal_with_history ("Expected GC Neutral mode but was in %s state", mono_thread_state_name (state));
}
}
void *
critical_gc_region_begin(void)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ return NULL;
+
CheckState *state = get_state ();
state->in_gc_critical_region++;
return state;
void
critical_gc_region_end(void* token)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ return;
+
CheckState *state = get_state();
g_assert (state == token);
state->in_gc_critical_region--;
void
assert_not_in_gc_critical_region(void)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ return;
+
CheckState *state = get_state();
if (state->in_gc_critical_region > 0) {
- assertion_fail("Expected GC Unsafe mode, but was in %s state", mono_thread_state_name (mono_thread_info_current_state (mono_thread_info_current ())));
+ mono_fatal_with_history("Expected GC Unsafe mode, but was in %s state", mono_thread_state_name (mono_thread_info_current_state (mono_thread_info_current ())));
}
}
void
assert_in_gc_critical_region (void)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ return;
+
CheckState *state = get_state();
if (state->in_gc_critical_region == 0)
- assertion_fail("Expected GC critical region");
+ mono_fatal_with_history("Expected GC critical region");
}
-#endif /* !defined(DISABLE_CHECKED_BUILD_GC) */
+#endif /* defined(ENABLE_CHECKED_BUILD_GC) */
-#if !defined(DISABLE_CHECKED_BUILD_METADATA)
+#ifdef ENABLE_CHECKED_BUILD_METADATA
// check_metadata_store et al: The goal of these functions is to verify that if there is a pointer from one mempool into
// another, that the pointed-to memory is protected by the reference count mechanism for MonoImages.
static void
check_mempool_may_reference_mempool (void *from_ptr, void *to_ptr, gboolean require_local)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_METADATA))
+ return;
+
// Null pointers are OK
if (!to_ptr)
return;
check_mempool_may_reference_mempool (from, to, TRUE);
}
-#endif /* !defined(DISABLE_CHECKED_BUILD_METADATA) */
+#endif /* defined(ENABLE_CHECKED_BUILD_METADATA) */
-#endif /* CHECKED_BUILD */
+#endif /* ENABLE_CHECKED_BUILD */
#include <config.h>
#include <mono/utils/atomic.h>
+#include <mono/utils/mono-publib.h>
+
+typedef enum {
+ MONO_CHECK_MODE_NONE = 0,
+ MONO_CHECK_MODE_GC = 0x1,
+ MONO_CHECK_MODE_METADATA = 0x2,
+ MONO_CHECK_MODE_THREAD = 0x4,
+ MONO_CHECK_MODE_ALL = MONO_CHECK_MODE_GC | MONO_CHECK_MODE_METADATA | MONO_CHECK_MODE_THREAD,
+ MONO_CHECK_MODE_UNKNOWN = 0x8
+} MonoCheckMode;
+
+mono_bool mono_check_mode_enabled (MonoCheckMode query);
// This is for metadata writes which we have chosen not to check at the current time.
// Because in principle this should never happen, we still use a macro so that the exemptions will be easier to find, and remove, later.
// The current reason why this is needed is for pointers to constant strings, which the checker cannot verify yet.
#define CHECKED_METADATA_WRITE_PTR_EXEMPT(ptr, val) do { (ptr) = (val); } while (0)
-#if defined(CHECKED_BUILD)
+#ifdef ENABLE_CHECKED_BUILD
#define g_assert_checked g_assert
#define CHECKED_MONO_INIT()
-#endif /* CHECKED_BUILD */
-
-#if defined(CHECKED_BUILD) && !defined(DISABLE_CHECKED_BUILD_GC)
+#endif /* ENABLE_CHECKED_BUILD */
+#ifdef ENABLE_CHECKED_BUILD_GC
/*
GC runtime modes rules:
#define MONO_REQ_GC_NOT_CRITICAL
#define MONO_REQ_GC_CRITICAL
-#endif /* defined(CHECKED_BUILD) && !defined(DISABLE_CHECKED_BUILD_GC) */
+#endif /* defined(ENABLE_CHECKED_BUILD_GC) */
-#if defined(CHECKED_BUILD) && !defined(DISABLE_CHECKED_BUILD_METADATA)
+#ifdef ENABLE_CHECKED_BUILD_METADATA
// Use when writing a pointer from one image or imageset to another.
#define CHECKED_METADATA_WRITE_PTR(ptr, val) do { \
#define CHECKED_METADATA_WRITE_PTR_LOCAL(ptr, val) do { (ptr) = (val); } while (0)
#define CHECKED_METADATA_WRITE_PTR_ATOMIC(ptr, val) do { mono_atomic_store_release (&(ptr), (val)); } while (0)
-#endif /* defined(CHECKED_BUILD) && !defined(DISABLE_CHECKED_BUILD_METADATA) */
+#endif /* defined(ENABLE_CHECKED_BUILD_METADATA) */
-#if defined(CHECKED_BUILD) && !defined(DISABLE_CHECKED_BUILD_THREAD)
+#ifdef ENABLE_CHECKED_BUILD_THREAD
#define CHECKED_BUILD_THREAD_TRANSITION(transition, info, from_state, suspend_count, next_state, suspend_count_delta) do { \
checked_build_thread_transition (transition, info, from_state, suspend_count, next_state, suspend_count_delta); \
void checked_build_thread_transition(const char *transition, void *info, int from_state, int suspend_count, int next_state, int suspend_count_delta);
+void mono_fatal_with_history(const char *msg, ...);
+
#else
#define CHECKED_BUILD_THREAD_TRANSITION(transition, info, from_state, suspend_count, next_state, suspend_count_delta)
-#endif /* defined(CHECKED_BUILD) && !defined(DISABLE_CHECKED_BUILD_THREAD) */
+#define mono_fatal_with_history g_error
+
+#endif /* defined(ENABLE_CHECKED_BUILD_THREAD) */
#endif /* __CHECKED_BUILD_H__ */
void
mono_error_set_generic_error (MonoError *error, const char * name_space, const char *name, const char *msg_format, ...);
+void
+mono_error_set_execution_engine (MonoError *error, const char *msg_format, ...);
+
+void
+mono_error_set_not_implemented (MonoError *error, const char *msg_format, ...);
+
+void
+mono_error_set_not_supported (MonoError *error, const char *msg_format, ...);
+
void
mono_error_set_exception_instance (MonoError *error, MonoException *exc);
#include <mono/metadata/object.h>
#include <mono/metadata/object-internals.h>
+#define set_error_messagev() do { \
+ if (!(error->full_message = g_strdup_vprintf (msg_format, args))) \
+ error->flags |= MONO_ERROR_INCOMPLETE; \
+} while (0)
+
#define set_error_message() do { \
va_list args; \
va_start (args, msg_format); \
- if (!(error->full_message = g_strdup_vprintf (msg_format, args))) \
- error->flags |= MONO_ERROR_INCOMPLETE; \
+ set_error_messagev(); \
va_end (args); \
} while (0)
+static void
+mono_error_set_generic_errorv (MonoError *oerror, const char *name_space, const char *name, const char *msg_format, va_list args);
+
static gboolean
is_managed_exception (MonoErrorInternal *error)
{
}
void
-mono_error_set_generic_error (MonoError *oerror, const char * name_space, const char *name, const char *msg_format, ...)
+mono_error_set_generic_errorv (MonoError *oerror, const char *name_space, const char *name, const char *msg_format, va_list args)
{
MonoErrorInternal *error = (MonoErrorInternal*)oerror;
mono_error_prepare (error);
error->error_code = MONO_ERROR_GENERIC;
mono_error_set_corlib_exception (oerror, name_space, name);
- set_error_message ();
+ set_error_messagev ();
+}
+
+void
+mono_error_set_generic_error (MonoError *oerror, const char * name_space, const char *name, const char *msg_format, ...)
+{
+ va_list args;
+ va_start (args, msg_format);
+ mono_error_set_generic_errorv (oerror, name_space, name, msg_format, args);
+ va_end (args);
+}
+
+/**
+ * mono_error_set_not_implemented:
+ *
+ * System.NotImplementedException
+ */
+void
+mono_error_set_not_implemented (MonoError *oerror, const char *msg_format, ...)
+{
+ va_list args;
+ va_start (args, msg_format);
+ mono_error_set_generic_errorv (oerror, "System", "NotImplementedException", msg_format, args);
+ va_end (args);
+}
+
+/**
+ * mono_error_set_execution_engine:
+ *
+ * System.ExecutionEngineException
+ */
+void
+mono_error_set_execution_engine (MonoError *oerror, const char *msg_format, ...)
+{
+ va_list args;
+ va_start (args, msg_format);
+ mono_error_set_generic_errorv (oerror, "System", "ExecutionEngineException", msg_format, args);
+ va_end (args);
+}
+
+/**
+ * mono_error_set_execution_engine:
+ *
+ * System.NotSupportedException
+ */
+void
+mono_error_set_not_supported (MonoError *oerror, const char *msg_format, ...)
+{
+ va_list args;
+ va_start (args, msg_format);
+ mono_error_set_generic_errorv (oerror, "System", "NotSupportedException", msg_format, args);
+ va_end (args);
}
void
mono_error_prepare (error);
if (!loader_error) {
- mono_error_set_generic_error (oerror, "System", "ExecutionEngineException", "Runtime tried to produce a mono-error from an empty loader-error");
+ mono_error_set_execution_engine (oerror, "Runtime tried to produce a mono-error from an empty loader-error");
return;
}
switch (loader_error->exception_type) {
case MONO_EXCEPTION_NONE:
- mono_error_set_generic_error (oerror, "System", "ExecutionEngineException", "Runtime tried to produce a mono-error from a non-error loader-error");
+ mono_error_set_execution_engine (oerror, "Runtime tried to produce a mono-error from a non-error loader-error");
break;
case MONO_EXCEPTION_INVALID_PROGRAM:
case MONO_EXCEPTION_OBJECT_SUPPLIED:
case MONO_EXCEPTION_GENERIC_SHARING_FAILED:
- mono_error_set_generic_error (oerror, "System", "ExecutionEngineException", "Runtime tried to produce a mono-error from JIT internal error %d", loader_error->exception_type);
+ mono_error_set_execution_engine (oerror, "Runtime tried to produce a mono-error from JIT internal error %d", loader_error->exception_type);
break;
case MONO_EXCEPTION_BAD_IMAGE:
break;
default:
- mono_error_set_generic_error (oerror, "System", "ExecutionEngineException", "Runtime tried to produce an unknown loader-error %d", loader_error->exception_type);
+ mono_error_set_execution_engine (oerror, "Runtime tried to produce an unknown loader-error %d", loader_error->exception_type);
break;
}
}
case MONO_ERROR_GENERIC:
if (!error->exception_name_space || !error->exception_name)
- mono_error_set_generic_error (error_out, "System", "ExecutionEngineException", "MonoError with generic error but no exception name was supplied");
+ mono_error_set_execution_engine (error_out, "MonoError with generic error but no exception name was supplied");
else
exception = mono_exception_from_name_msg (mono_defaults.corlib, error->exception_name_space, error->exception_name, error->full_message);
break;
break;
default:
- mono_error_set_generic_error (error_out, "System", "ExecutionEngineException", "Invalid error-code %d", error->error_code);
+ mono_error_set_execution_engine (error_out, "Invalid error-code %d", error->error_code);
}
if (!mono_error_ok (error_out))
#define mono_atomic_load_release(_type,target) ({ \
_type __tmp; \
LOAD_RELEASE_FENCE; \
- __tmp = *target; \
+ __tmp = *(target); \
__tmp; })
#define mono_atomic_load_acquire(var,_type,target) do { \
- _type __tmp = *target; \
+ _type __tmp = *(target); \
LOAD_ACQUIRE_FENCE; \
(var) = __tmp; \
} while (0)
#define mono_atomic_store_acquire(target,value) { \
- *target = value; \
+ *(target) = (value); \
STORE_ACQUIRE_FENCE; \
}
/* exception will be thrown in managed code */
CryptReleaseContext (provider, 0);
*handle = 0;
- mono_error_set_generic_error (error, "System", "ExecutionEngineException", "Failed to gen random bytes (%d)", GetLastError ());
+ mono_error_set_execution_engine (error, "Failed to gen random bytes (%d)", GetLastError ());
return FALSE;
}
}
if (file >= 0)
close (file);
g_warning ("Entropy problem! Can't create or connect to egd socket %s", path);
- mono_error_set_generic_error (error, "System", "ExecutionEngineException", "Failed to open egd socket %s: %s", path, strerror (err));
+ mono_error_set_execution_engine (error, "Failed to open egd socket %s: %s", path, strerror (err));
return;
}
} else {
close (file);
g_warning ("Send egd request failed %d", err);
- mono_error_set_generic_error (error, "System", "ExecutionEngineException", "Failed to send request to egd socket: %s", strerror (err));
+ mono_error_set_execution_engine (error, "Failed to send request to egd socket: %s", strerror (err));
return;
}
}
} else {
close (file);
g_warning ("Receive egd request failed %d", err);
- mono_error_set_generic_error (error, "System", "ExecutionEngineException", "Failed to get response from egd socket: %s", strerror(err));
+ mono_error_set_execution_engine (error, "Failed to get response from egd socket: %s", strerror(err));
return;
}
}
continue;
g_warning("Entropy error! Error in read (%s).", strerror (errno));
/* exception will be thrown in managed code */
- mono_error_set_generic_error (error, "System", "ExecutionEngineException", "Entropy error! Error in read (%s).", strerror (errno));
+ mono_error_set_execution_engine (error, "Entropy error! Error in read (%s).", strerror (errno));
return FALSE;
}
count += err;
#include "config.h"
-#ifdef ENABLE_EXTENSION_MODULE
-#include "../../../mono-extensions/mono/utils/mono-signal-handler.h"
+/*
+ * When a signal is delivered to a thread on a Krait Android device
+ * that's in the middle of skipping over an "IT" block, such as this
+ * one:
+ *
+ * 0x40184ef0 <dlfree+1308>: ldr r1, [r3, #0]
+ * 0x40184ef2 <dlfree+1310>: add.w r5, r12, r2, lsl #3
+ * 0x40184ef6 <dlfree+1314>: lsls.w r2, r0, r2
+ * 0x40184efa <dlfree+1318>: tst r2, r1
+ * ### this is the IT instruction
+ * 0x40184efc <dlfree+1320>: itt eq
+ * 0x40184efe <dlfree+1322>: orreq r2, r1
+ * ### signal arrives here
+ * 0x40184f00 <dlfree+1324>: streq r2, [r3, #0]
+ * 0x40184f02 <dlfree+1326>: beq.n 0x40184f1a <dlfree+1350>
+ * 0x40184f04 <dlfree+1328>: ldr r2, [r5, #8]
+ * 0x40184f06 <dlfree+1330>: ldr r3, [r3, #16]
+ *
+ * then the first few (at most four, one would assume) instructions of
+ * the signal handler (!) might be skipped. They happen to be the
+ * push of the frame pointer and return address, so once the signal
+ * handler has done its work, it returns into a SIGSEGV.
+ */
+
+#if defined (TARGET_ARM) && defined (HAVE_ARMV7) && defined (TARGET_ANDROID)
+#define KRAIT_IT_BUG_WORKAROUND 1
+#endif
+
+#ifdef KRAIT_IT_BUG_WORKAROUND
+#define MONO_SIGNAL_HANDLER_FUNC(access, name, arglist) \
+ static void __krait_ ## name arglist; \
+ __attribute__ ((naked)) access void \
+ name arglist \
+ { \
+ asm volatile ( \
+ "mov r0, r0\n\t" \
+ "mov r0, r0\n\t" \
+ "mov r0, r0\n\t" \
+ "mov r0, r0\n\t"); \
+ asm volatile ( \
+ "bx %0" \
+ : : "r" (__krait_ ## name)); \
+ } \
+ static void __krait_ ## name arglist
#endif
+
/* Don't use this */
#ifndef MONO_SIGNAL_HANDLER_FUNC
#define MONO_SIGNAL_HANDLER_FUNC(access, name, arglist) access void name arglist
#include <mono/utils/mono-counters.h>
#include <mono/utils/mono-threads-coop.h>
#include <mono/utils/mono-threads-api.h>
+#include <mono/utils/checked-build.h>
#ifdef TARGET_OSX
#include <mono/utils/mach-support.h>
volatile size_t mono_polling_required;
+// FIXME: This would be more efficient if instead of instantiating the stack it just pushed a simple depth counter up and down,
+// perhaps with a per-thread cookie in the high bits.
+#ifdef ENABLE_CHECKED_BUILD_GC
+// Maintains a single per-thread stack of ints, used to ensure nesting is not violated
+MonoNativeTlsKey coop_reset_count_stack_key;
+static int coop_tls_push (int v) {
+ GArray *stack = mono_native_tls_get_value (coop_reset_count_stack_key);
+ if (!stack) {
+ stack = g_array_new (FALSE,FALSE,sizeof(int));
+ mono_native_tls_set_value (coop_reset_count_stack_key, stack);
+ }
+ g_array_append_val (stack, v);
+ return stack->len;
+}
+static int coop_tls_pop (int *v) {
+ GArray *stack = mono_native_tls_get_value (coop_reset_count_stack_key);
+ if (!stack || 0 == stack->len)
+ return -1;
+ stack->len--;
+ *v = g_array_index (stack, int, stack->len);
+ int len = stack->len;
+ if (0 == len) {
+ g_array_free (stack,TRUE);
+ mono_native_tls_set_value (coop_reset_count_stack_key, NULL);
+ }
+ return len;
+}
+#endif
+
static int coop_reset_blocking_count;
static int coop_try_blocking_count;
static int coop_do_blocking_count;
if (!mono_threads_is_coop_enabled ())
return NULL;
+ info = mono_thread_info_current_unchecked ();
+
+#ifdef ENABLE_CHECKED_BUILD_GC
+ int reset_blocking_count = InterlockedIncrement (&coop_reset_blocking_count);
+ // In this mode, the blocking count is used as the reset cookie. We would prefer
+ // (but do not require) this to be unique across invocations and threads.
+ if (reset_blocking_count == 0) // We *do* require it be nonzero
+ reset_blocking_count = coop_reset_blocking_count = 1;
+#else
++coop_reset_blocking_count;
+#endif
- info = mono_thread_info_current_unchecked ();
/* If the thread is not attached, it doesn't make sense prepare for suspend. */
if (!info || !mono_thread_info_is_live (info))
return NULL;
return NULL;
case AbortBlockingOk:
info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
- return info;
+ break;
case AbortBlockingOkAndPool:
mono_threads_state_poll ();
- return info;
+ break;
default:
g_error ("Unknown thread state");
}
+
+#ifdef ENABLE_CHECKED_BUILD_GC
+ if (mono_check_mode_enabled (MONO_CHECK_MODE_GC)) {
+ int level = coop_tls_push (reset_blocking_count);
+ //g_warning("Entering reset nest; level %d; cookie %d\n", level, reset_blocking_count);
+ return (void *)(intptr_t)reset_blocking_count;
+ }
+#endif
+
+ return info;
}
void
mono_threads_reset_blocking_end (void *cookie, void* stackdata)
{
- MonoThreadInfo *info;
-
if (!mono_threads_is_coop_enabled ())
return;
- info = (MonoThreadInfo *)cookie;
- if (!info)
+ if (!cookie)
return;
- g_assert (info == mono_thread_info_current_unchecked ());
+#ifdef ENABLE_CHECKED_BUILD_GC
+ if (mono_check_mode_enabled (MONO_CHECK_MODE_GC)) {
+ int received_cookie = (int)(intptr_t)cookie;
+ int desired_cookie;
+ int level = coop_tls_pop (&desired_cookie);
+ //g_warning("Leaving reset nest; back to level %d; desired cookie %d; received cookie %d\n", level, desired_cookie, received_cookie);
+ if (level < 0)
+ mono_fatal_with_history ("Expected cookie %d but found no stack at all\n", desired_cookie);
+ if (desired_cookie != received_cookie)
+ mono_fatal_with_history ("Expected cookie %d but received %d\n", desired_cookie, received_cookie);
+ } else // Notice this matches the line after the endif
+#endif
+ {
+ g_assert (((MonoThreadInfo *)cookie) == mono_thread_info_current_unchecked ());
+ }
+
mono_threads_prepare_blocking (stackdata);
}
mono_counters_register ("Coop Do Polling", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_polling_count);
mono_counters_register ("Coop Save Count", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_save_count);
//See the above for what's wrong here.
+
+#ifdef ENABLE_CHECKED_BUILD_GC
+ mono_native_tls_alloc (&coop_reset_count_stack_key, NULL);
+#endif
}
void
trace_state_change ("ATTACH", info, raw_state, STATE_RUNNING, 0);
break;
default:
- g_error ("Cannot transition current thread from %s with ATTACH", state_name (cur_state));
+ mono_fatal_with_history ("Cannot transition current thread from %s with ATTACH", state_name (cur_state));
}
}
STATE_BLOCKING_AND_SUSPENDED: This is a bug in coop x suspend that resulted the thread in an undetachable state.
*/
default:
- g_error ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
+ mono_fatal_with_history ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
}
}
If this turns to be an issue we can introduce a new suspend request state for when both have been requested.
*/
default:
- g_error ("Cannot transition thread %p from %s with SUSPEND_REQUEST", mono_thread_info_get_tid (info), state_name (cur_state));
+ mono_fatal_with_history ("Cannot transition thread %p from %s with SUSPEND_REQUEST", mono_thread_info_get_tid (info), state_name (cur_state));
}
}
STATE_ASYNC_SUSPEND_REQUESTED: Since there can only be one async suspend in progress and it must finish, it should not be possible to witness this.
*/
default:
- g_error ("Cannot transition thread %p from %s with ASYNC_SUSPEND_REQUESTED", mono_thread_info_get_tid (info), state_name (cur_state));
+ mono_fatal_with_history ("Cannot transition thread %p from %s with ASYNC_SUSPEND_REQUESTED", mono_thread_info_get_tid (info), state_name (cur_state));
}
return (MonoRequestAsyncSuspendResult) FALSE;
}
STATE_BLOCKING_AND_SUSPENDED: Pool is a local state transition. No VM activities are allowed while in blocking mode.
*/
default:
- g_error ("Cannot transition thread %p from %s with STATE_POLL", mono_thread_info_get_tid (info), state_name (cur_state));
+ mono_fatal_with_history ("Cannot transition thread %p from %s with STATE_POLL", mono_thread_info_get_tid (info), state_name (cur_state));
}
}
*/
default:
- g_error ("Cannot transition thread %p from %s with REQUEST_RESUME", mono_thread_info_get_tid (info), state_name (cur_state));
+ mono_fatal_with_history ("Cannot transition thread %p from %s with REQUEST_RESUME", mono_thread_info_get_tid (info), state_name (cur_state));
}
}
STATE_BLOCKING: Async suspend only begins if a transition to async suspend requested happened. Blocking would have put us into blocking with positive suspend count if it raced with async finish.
*/
default:
- g_error ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
+ mono_fatal_with_history ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
}
}
STATE_SELF_SUSPEND_REQUESTED: All those are invalid end states of a sucessfull finish async suspend
*/
default:
- g_error ("Cannot transition thread %p from %s with COMPENSATE_FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
+ mono_fatal_with_history ("Cannot transition thread %p from %s with COMPENSATE_FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
}
}
STATE_BLOCKING_AND_SUSPENDED: Blocking is not nestabled
*/
default:
- g_error ("Cannot transition thread %p from %s with DO_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
+ mono_fatal_with_history ("Cannot transition thread %p from %s with DO_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
}
}
STATE_BLOCKING_AND_SUSPENDED: This an exit state of done blocking
*/
default:
- g_error ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
+ mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
}
}
STATE_BLOCKING_AND_SUSPENDED: This is an exit state of done blocking, can't happen here.
*/
default:
- g_error ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
+ mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
}
}
<ClCompile Include="..\mono\sgen\sgen-pinning-stats.c" />\r
<ClCompile Include="..\mono\sgen\sgen-pinning.c" />\r
<ClCompile Include="..\mono\sgen\sgen-pointer-queue.c" />\r
+ <ClCompile Include="..\mono\sgen\sgen-array-list.c" />\r
<ClCompile Include="..\mono\sgen\sgen-protocol.c" />\r
<ClCompile Include="..\mono\sgen\sgen-qsort.c" />\r
<ClCompile Include="..\mono\sgen\sgen-simple-nursery.c" />\r
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
<ImportGroup Label="ExtensionTargets">\r
</ImportGroup>\r
-</Project>
\ No newline at end of file
+</Project>\r
endif BUILD_MCS
-TEST_SUPPORT_FILES = $(tmpinst)/bin/mono $(tmpinst)/bin/ilasm $(tmpinst)/bin/mcs $(tmpinst)/bin/dmcs $(tmpinst)/bin/al2 $(tmpinst)/bin/al
+TEST_SUPPORT_FILES = $(tmpinst)/bin/mono $(tmpinst)/bin/ilasm $(tmpinst)/bin/mcs $(tmpinst)/bin/al
mcs-do-test-profiles:
cd $(mcs_topdir) && $(MAKE) NO_DIR_CHECK=1 PROFILES='$(test_profiles)' test-profiles
echo 'exec "'"$$r/$(tmpinst)/bin/mono"'" "'"$$m/class/lib/build/mcs.exe"'" "$$@"' >> $@ ; \
chmod +x $@
-$(tmpinst)/bin/dmcs: $(tmpinst)/bin/mono Makefile
- echo '#! /bin/sh' > $@ ; \
- r=`pwd`; m=`cd $(mcs_topdir) && pwd`; \
- echo 'exec "'"$$r/$(tmpinst)/bin/mono"'" "'"$$m/class/lib/build/mcs.exe -sdk:4"'" "$$@"' >> $@ ; \
- chmod +x $@
-
$(tmpinst)/bin/ilasm: $(tmpinst)/bin/mono Makefile
echo '#! /bin/sh' > $@ ; \
r=`pwd`; m=`cd $(mcs_topdir) && pwd`; \
echo 'exec "'"$$r/$(tmpinst)/bin/mono"'" "'"$$m/ilasm/ilasm.exe"'" "$$@"' >> $@ ; \
chmod +x $@
-$(tmpinst)/bin/al2: $(tmpinst)/bin/mono Makefile
- echo '#! /bin/sh' > $@ ; \
- r=`pwd`; m=`cd $(mcs_topdir) && pwd`; \
- echo 'exec "'"$$r/$(tmpinst)/bin/mono"'" "'"$$m/class/lib/net_2_0/al.exe"'" "$$@"' >> $@ ; \
- chmod +x $@
-
$(tmpinst)/bin/al: $(tmpinst)/bin/mono Makefile
echo '#! /bin/sh' > $@ ; \
r=`pwd`; m=`cd $(mcs_topdir) && pwd`; \
/mono-abi-info
/mono-api-diff
/mono-api-info
+/mono-api-html
/mono-api-info1
/mono-api-info2
/mono-cil-strip
mod$(SCRIPT_SUFFIX) \
monolinker$(SCRIPT_SUFFIX) \
mono-api-info$(SCRIPT_SUFFIX) \
+ mono-api-html$(SCRIPT_SUFFIX) \
mono-shlib-cop$(SCRIPT_SUFFIX) \
mozroots$(SCRIPT_SUFFIX) \
permview$(SCRIPT_SUFFIX) \