Skip to content

Cleanup error marshaling and generating code #183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 10 additions & 17 deletions LibGit2Sharp/Core/Ensure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,26 +62,19 @@ public static void Success(int result, bool allowPositiveResult = false)
return;
}

string errorMessage;
GitError error = NativeMethods.giterr_last().MarshalAsGitError();


var error = NativeMethods.giterr_last();
if (error == null)
{
error = new GitError { Klass = GitErrorType.Unknown, Message = IntPtr.Zero };
errorMessage = "No error message has been provided by the native library";
}
else
{
errorMessage = Utf8Marshaler.FromNative(error.Message);
throw new LibGit2SharpException(
(GitErrorCode)result,
GitErrorCategory.Unknown,
"No error message has been provided by the native library");
}

throw new LibGit2SharpException(
String.Format(CultureInfo.InvariantCulture, "An error was raised by libgit2. Class = {0} ({1}).{2}{3}",
error.Klass,
result,
Environment.NewLine,
errorMessage));
(GitErrorCode)result,
error.Category,
Utf8Marshaler.FromNative(error.Message));
}

/// <summary>
Expand All @@ -108,8 +101,8 @@ public static void GitObjectIsNotNull(GitObject gitObject, string identifier)
}

throw new LibGit2SharpException(string.Format(CultureInfo.InvariantCulture,
"No valid git object identified by '{0}' exists in the repository.",
identifier));
"No valid git object identified by '{0}' exists in the repository.",
identifier));
}
}
}
2 changes: 1 addition & 1 deletion LibGit2Sharp/Core/GitError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ namespace LibGit2Sharp.Core
internal class GitError
{
public IntPtr Message;
public GitErrorType Klass;
public GitErrorCategory Category;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace LibGit2Sharp.Core
{
internal enum GitErrorType
public enum GitErrorCategory
{
Unknown = -1,
NoMemory,
Expand Down
2 changes: 1 addition & 1 deletion LibGit2Sharp/Core/GitErrorCode.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace LibGit2Sharp.Core
{
internal enum GitErrorCode
public enum GitErrorCode
{
Ok = 0,
Error = -1,
Expand Down
47 changes: 47 additions & 0 deletions LibGit2Sharp/Core/GitErrorMarshaler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Runtime.InteropServices;

namespace LibGit2Sharp.Core
{
internal class GitErrorMarshaler : ICustomMarshaler
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Classy!

{
static readonly GitErrorMarshaler staticInstance = new GitErrorMarshaler();

public void CleanUpManagedData(object managedObj)
{
}

public void CleanUpNativeData(IntPtr pNativeData)
{
if (pNativeData != IntPtr.Zero)
{
Marshal.FreeHGlobal(pNativeData);
}
}

public int GetNativeDataSize()
{
return -1;
}

public IntPtr MarshalManagedToNative(object managedObj)
{
throw new NotImplementedException();
}

public object MarshalNativeToManaged(IntPtr pNativeData)
{
return NativeToGitError(pNativeData);
}

protected GitError NativeToGitError(IntPtr pNativeData)
{
return (GitError)Marshal.PtrToStructure(pNativeData, typeof(GitError));
}

public static ICustomMarshaler GetInstance(string cookie)
{
return staticInstance;
}
}
}
12 changes: 0 additions & 12 deletions LibGit2Sharp/Core/Handles/GitErrorSafeHandle.cs

This file was deleted.

3 changes: 2 additions & 1 deletion LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public static bool RepositoryStateChecker(RepositorySafeHandle repositoryPtr, Fu
}

[DllImport(libgit2)]
public static extern GitErrorSafeHandle giterr_last();
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(GitErrorMarshaler))]
public static extern GitError giterr_last();

[DllImport(libgit2)]
public static extern int git_blob_create_fromdisk(
Expand Down
4 changes: 2 additions & 2 deletions LibGit2Sharp/LibGit2Sharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@
<Compile Include="Core\GitDiff.cs" />
<Compile Include="Core\GitDiffExtensions.cs" />
<Compile Include="Core\GitError.cs" />
<Compile Include="Core\GitErrorType.cs" />
<Compile Include="Core\GitErrorCategory.cs" />
<Compile Include="Core\GitErrorMarshaler.cs" />
<Compile Include="Core\GitNoteData.cs" />
<Compile Include="Core\GitObjectExtensions.cs" />
<Compile Include="Core\Handles\GitErrorSafeHandle.cs" />
<Compile Include="Core\Handles\NoteSafeHandle.cs" />
<Compile Include="Core\Handles\ObjectDatabaseSafeHandle.cs" />
<Compile Include="Core\Handles\DiffListSafeHandle.cs" />
Expand Down
45 changes: 38 additions & 7 deletions LibGit2Sharp/LibGit2SharpException.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Runtime.Serialization;
using System.Globalization;
using LibGit2Sharp.Core;

namespace LibGit2Sharp
{
Expand Down Expand Up @@ -39,16 +40,29 @@ public LibGit2Exception(string message, Exception innerException)
/// <summary>
/// The exception that is thrown when an error occurs during application execution.
/// </summary>
[Serializable]
public class LibGit2SharpException : Exception
{
readonly GitErrorCode code;
readonly GitErrorCategory category;
readonly bool isLibraryError;

/// <summary>
/// Initializes a new instance of the <see cref = "LibGit2SharpException" /> class.
/// </summary>
public LibGit2SharpException()
{
}

/// <summary>
/// Initializes a new instance of the <see cref = "LibGit2SharpException" /> class.
/// </summary>
public LibGit2SharpException(GitErrorCode code, GitErrorCategory category, string message) : base(message)
{
Data["libgit2.code"] = this.code = code;
Data["libgit2.class"] = this.category = category;
isLibraryError = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you stop exposing the GitErrorCode as a strongly typed property? We actually reference that in GHfW. Am I supposed to reach into Data["libgit2.code"] instead? That makes the code uglier and feels more fragile.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you stop exposing the GitErrorCode as a strongly typed property?

I'm not sure to follow you. GitErrorCode has been turned Internal more than a year ago (cf 2bd0630)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I think in our branch we exposed it as a property. Why would you make this internal and not give me anything else I can use to know exactly what the error is? Parsing exception messages is piss poor and likely to change. Reaching into the Data property is ugly too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The advantage of Data is that it's automatically serialized, whereas custom properties are not. What about public properties that encapsulate retrieving the code/class from Data?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dahlbyk Sure. As a consumer of the API, I sort of don't care what the backing storage of the properties are. As it stands, I have my own extension methods that do this, but it seems like a no-brainer to have it in the class as part of the API.

Another option is to implement the serialization constructors for the exception type per FX Design Guidelines: http://msdn.microsoft.com/en-us/library/ms229064.aspx

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementing deserialization isn't hard, it's just more effort than using Data.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I really don't care how it's done. :)

But while we're on the topic, it does seem odd to me because Data is read/write, no? So that would allow any code along the stack to modify properties in there. Thus if you created GitErrorCode as a wrapper property, it wouldn't really be readonly, though semantically that's exactly what I'd expect.

All for a little less effort?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@haacked

All for a little less effort?

It's not about avoiding implementing the deserialization. Although that's a nice side benefit :)

You can peek at the following comments for a better context

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nulltoken ah, thanks for the context! I like the idea of exposing specific exception classes. But I didn't see such classes yet.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@haacked You're right. They have to be created yet as discussed in #137.

}

/// <summary>
/// Initializes a new instance of the <see cref = "LibGit2SharpException" /> class with a specified error message.
/// </summary>
Expand All @@ -69,13 +83,30 @@ public LibGit2SharpException(string message, Exception innerException)
}

/// <summary>
/// Initializes a new instance of the <see cref = "LibGit2SharpException" /> class with a serialized data.
/// The specific libgit2 error code.
/// </summary>
/// <param name = "info">The <see cref="SerializationInfo "/> that holds the serialized object data about the exception being thrown.</param>
/// <param name = "context">The <see cref="StreamingContext"/> that contains contextual information about the source or destination.</param>
protected LibGit2SharpException(SerializationInfo info, StreamingContext context)
: base(info, context)
public GitErrorCode Code
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tclem @haacked As previously discussed, I'm really not willing to make the GitErrorCode and GitErrorCategory types publicly exposed, nor to expose such properties. Indeed, I don't feel the grain is fine enough (yet?) to safely build a stable/long lasting error handling strategy.

Therefore, for now, I'd prefer to only expose int typed values in the Data dictionary.
This makes the libgit2 native error codes available for logging purpose.

In order to reduce the pain, we'll indeed have to add proper exceptions to LibGit2Sharp. Which one would you see fit?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tclem @haacked I've created #189 to cope with the lack of proper exceptions issue

{
get { return Data.Contains("libgit2.code") ? (GitErrorCode)Data["libgit2.code"] : GitErrorCode.Error; }
}

/// <summary>
/// The specific libgit2 error class.
/// </summary>
public GitErrorCategory Category
{
get { return Data.Contains("libgit2.class") ? (GitErrorCategory)Data["libgit2.class"] : GitErrorCategory.Unknown; }
}

public override string ToString()
{
return isLibraryError
? String.Format(CultureInfo.InvariantCulture, "An error was raised by libgit2. Class = {0} ({1}).{2}{3}",
category,
code,
Environment.NewLine,
Message)
: base.ToString();
}
}
}