Skip to content
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

Explicitly include an enum by name #33

Merged
merged 3 commits into from
Sep 1, 2024
Merged
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ obj/
/artifacts/
src/c/tests/**/ffi/*.json
src/c/tests/**/ffi-x/*.json

# Native library files
lib/*.*
30 changes: 30 additions & 0 deletions src/c/tests/functions/function_implicit_enum/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"inputFilePath": "./main.c",
"userIncludeDirectories": [
"../../../production/ffi_helper/include"
],
"ignoredIncludeFiles": [
"../../../production/ffi_helper/include/ffi_helper.h"
],
"includedNames": [
"enum_implicit"
],
"targetPlatforms": {
"windows": {
"i686-pc-windows-msvc": {},
"x86_64-pc-windows-msvc": {},
"aarch64-pc-windows-msvc": {}
},
"macos": {
"i686-apple-darwin": {},
"aarch64-apple-darwin": {},
"x86_64-apple-darwin": {},
"aarch64-apple-ios": {}
},
"linux": {
"i686-unknown-linux-gnu": {},
"x86_64-unknown-linux-gnu": {},
"aarch64-unknown-linux-gnu": {}
}
}
}
12 changes: 12 additions & 0 deletions src/c/tests/functions/function_implicit_enum/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <stdio.h>
#include "ffi_helper.h"

enum enum_implicit {
ENUM_IMPLICIT_VALUE0 = 0,
ENUM_IMPLICIT_VALUE1 = 255
} enum_implicit;

FFI_API_DECL int function_implicit_enum(int value)
{
return ENUM_IMPLICIT_VALUE1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace c2ffi.Data.Serialization
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "8.0.10.11423")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "8.0.10.31311")]
public partial class JsonSerializerContextCFfiCrossPlatform
{
private readonly static global::System.Text.Json.JsonSerializerOptions s_defaultOptions = new()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace c2ffi.Data.Serialization
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "8.0.10.11423")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "8.0.10.31311")]
public partial class JsonSerializerContextCFfiTargetPlatform
{
private readonly static global::System.Text.Json.JsonSerializerOptions s_defaultOptions = new()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,24 @@ public void Dispose()
ParseContext.Dispose();
}

public string GetFieldName(clang.CXCursor clangCursor)
{
if (clangCursor.kind != clang.CXCursorKind.CXCursor_FieldDecl)
{
return string.Empty;
}

var name = clangCursor.Spelling();

// NOTE: In newer versions of Clang (specifically found on 18.1), getting the name of the field has changed if it anonymous. Old behavior was to return empty string.
if (name.Contains("::(anonymous at", StringComparison.OrdinalIgnoreCase))
{
return string.Empty;
}

return name;
}

private CType VisitTypeInternal(
CNodeKind nodeKind,
string typeName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ public sealed class ExploreNodeInfo

public override string ToString()
{
return Name;
if (!string.IsNullOrEmpty(Name))
{
return Name;
}

if (!string.IsNullOrEmpty(TypeName))
{
return TypeName;
}

return "???";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,18 @@ public CFfiTargetPlatform ExtractFfi(
private void VisitTranslationUnit(ExploreContext context, ParseContext parseContext)
{
LogVisitingTranslationUnit(parseContext.FilePath);
VisitIncludes(context, parseContext);

var isMultipleHeaders = !context.ParseContext.ExtractInput.IsSingleHeader;
if (isMultipleHeaders)
{
VisitIncludes(context, parseContext);
}

VisitFunctions(context, parseContext);
VisitVariables(context, parseContext);
VisitMacroObjects(context, parseContext);
VisitExplicitIncludedNames(context, parseContext);

LogVisitedTranslationUnit(parseContext.FilePath);
}

Expand Down Expand Up @@ -118,6 +126,33 @@ private void VisitMacroObject(ExploreContext context, clang.CXCursor clangCursor
context.TryEnqueueNode(info);
}

private void VisitExplicitIncludedNames(ExploreContext context, ParseContext parseContext)
{
var cursors = parseContext.GetExplicitlyIncludedNamedCursors();
foreach (var cursor in cursors)
{
VisitExplicitlyIncludedName(context, cursor);
}
}

private void VisitExplicitlyIncludedName(ExploreContext context, clang.CXCursor clangCursor)
{
var nodeKind = clangCursor.kind switch
{
clang.CXCursorKind.CXCursor_EnumDecl => CNodeKind.Enum,
_ => CNodeKind.Unknown
};

if (nodeKind == CNodeKind.Unknown)
{
// TODO: Add more allowed kinds to explicitly included names
return;
}

var info = context.CreateTopLevelNodeInfo(nodeKind, clangCursor);
context.TryEnqueueNode(info);
}

private void VisitIncludes(ExploreContext context, ParseContext parseContext)
{
var includeCursors = parseContext.GetIncludes();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ private CRecordField StructField(
ExploreNodeInfo structInfo,
CXCursor clangCursor)
{
var fieldName = clangCursor.Spelling();
var fieldName = context.GetFieldName(clangCursor);
var clangType = clang_getCursorType(clangCursor);
var location = context.ParseContext.Location(clangCursor);
var type = context.VisitType(clangType, structInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ private CRecordField UnionField(
CXCursor clangCursor,
ExploreNodeInfo parentInfo)
{
var name = clangCursor.Spelling();
var name = context.GetFieldName(clangCursor);
var clangType = clang_getCursorType(clangCursor);
var location = context.ParseContext.Location(clangCursor);
var type = context.VisitType(clangType, parentInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ private static unsafe bool TryParseTranslationUnit(
ImmutableArray<string> commandLineArgs,
out CXTranslationUnit translationUnit,
bool skipFunctionBodies = true,
bool keepGoing = false)
bool keepGoing = false,
bool isSingleHeader = false)
{
// ReSharper disable BitwiseOperatorOnEnumWithoutFlags
uint options = 0x0 |
Expand All @@ -79,6 +80,11 @@ private static unsafe bool TryParseTranslationUnit(
0x4000 | // CXTranslationUnit_IgnoreNonErrorsFromIncludedFiles
0x0;

if (isSingleHeader)
{
options |= 0x400; // CXTranslationUnit_SingleFileParse
}

if (skipFunctionBodies)
{
options |= 0x40; // CXTranslationUnit_SkipFunctionBodies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,24 @@ static bool IsExternalVariable(clang.CXCursor child, clang.CXCursor parent)
return false;
}

return child.kind == clang.CXCursorKind.CXCursor_VarDecl && IsExternal(child);
var isExternal = IsExternal(child);
var isVariable = child.kind == clang.CXCursorKind.CXCursor_VarDecl;
return isVariable && isExternal;
}
}

public ImmutableArray<clang.CXCursor> GetExplicitlyIncludedNamedCursors()
{
var translationUnitCursor = clang.clang_getTranslationUnitCursor(_translationUnit);
var result = translationUnitCursor.GetDescendents(
(cursor, parent) => IsExplicitlyIncludedName(cursor, ExtractInput.IncludedNames));
return result;

static bool IsExplicitlyIncludedName(clang.CXCursor cursor, ImmutableHashSet<string> names)
{
var name = cursor.Spelling();
var isIncluded = names.Contains(name);
return isIncluded;
}
}

Expand Down Expand Up @@ -278,12 +295,6 @@ private void TryReleaseUnmanagedResources()

private static bool IsExternal(clang.CXCursor cursor)
{
var spelling = cursor.Spelling();
if (spelling == "SDL_malloc")
{
Console.WriteLine();
}

var linkage = clang.clang_getCursorLinkage(cursor);
if (linkage == clang.CXLinkageKind.CXLinkage_Invalid)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ private ExtractTargetPlatformInput SanitizeTargetPlatformInput(
MacroObjectDefines = ClangDefines(input, targetPlatformInput),
AdditionalArguments = ClangArguments(targetPlatformInput),
IsEnabledFindSystemHeaders = input.IsEnabledAutomaticallyFindSystemHeaders ?? true,
IsSingleHeader = input.IsSingleHeader ?? false,
IncludedNames = IncludedNames(input),
IgnoredMacroObjectsRegexes = IgnoredMacroObjects(input),
IgnoredVariableRegexes = IgnoredVariables(input),
IgnoredFunctionRegexes = IgnoredFunctions(input)
Expand Down Expand Up @@ -194,6 +196,11 @@ private ImmutableArray<Regex> IgnoredFunctions(UnsanitizedExtractInput input)
return SanitizeRegexes(input.IgnoredFunctions);
}

private ImmutableHashSet<string> IncludedNames(UnsanitizedExtractInput input)
{
return SanitizeStrings(input.IncludedNames).ToImmutableHashSet();
}

private string SanitizeOutputDirectoryPath(
string? outputDirectoryPath,
string targetPlatformString)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Immutable;
using System.Text.RegularExpressions;
using bottlenoselabs;
using c2ffi.Data;

namespace c2ffi.Tool.Commands.Extract.Input.Sanitized;
Expand All @@ -25,6 +26,10 @@ public sealed class ExtractTargetPlatformInput

public bool IsEnabledFindSystemHeaders { get; init; }

public bool IsSingleHeader { get; init; }

public ImmutableHashSet<string> IncludedNames { get; init; } = ImmutableHashSet<string>.Empty;

public ImmutableArray<Regex> IgnoredMacroObjectsRegexes { get; init; } = ImmutableArray<Regex>.Empty;

public ImmutableArray<Regex> IgnoredVariableRegexes { get; init; } = ImmutableArray<Regex>.Empty;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,45 +54,6 @@ public sealed class UnsanitizedExtractInput : ToolUnsanitizedInput
[JsonPropertyName("ignoredIncludeFiles")]
public ImmutableArray<string>? IgnoredIncludeFiles { get; set; }

/// <summary>
/// Gets or sets a value that determines whether to show the the path of header code locations with full paths
/// or relative paths.
/// </summary>
/// <remarks>
/// <para>
/// Default is <c>false</c>. Use <c>true</c> to use the full path for header locations. Use <c>false</c> to
/// show only relative file paths.
/// </para>
/// </remarks>
[JsonPropertyName("isEnabledLocationFullPaths")]
public bool? IsEnabledLocationFullPaths { get; set; }

/// <summary>
/// Gets or sets a value that determines whether to include or exclude declarations (functions, enums, structs,
/// typedefs, etc) with a prefixed underscore that are assumed to be 'non public'.
/// </summary>
/// <remarks>
/// <para>
/// Default is <c>false</c>. Use <c>true</c> to include declarations with a prefixed underscore. Use
/// <c>false</c> to exclude declarations with a prefixed underscore.
/// </para>
/// </remarks>
[JsonPropertyName("isEnabledAllowNamesWithPrefixedUnderscore")]
public bool? IsEnabledAllowNamesWithPrefixedUnderscore { get; set; }

/// <summary>
/// Gets or sets a value that determines whether to include or exclude system declarations (functions, enums,
/// typedefs, records, etc).
/// </summary>
/// <remarks>
/// <para>
/// Default is `false`. Use <c>true</c> to include system declarations. Use `false` to exclude system
/// declarations.
/// </para>
/// </remarks>
[JsonPropertyName("isEnabledSystemDeclarations")]
public bool? IsEnabledSystemDeclarations { get; set; }

/// <summary>
/// Gets or sets a value that determines whether to automatically find and append the system headers for the
/// target platform.
Expand All @@ -107,15 +68,14 @@ public sealed class UnsanitizedExtractInput : ToolUnsanitizedInput
public bool? IsEnabledAutomaticallyFindSystemHeaders { get; set; }

/// <summary>
/// Gets or sets determines whether to parse only the top-level cursors which are externally visible, or all
/// top-level cursors.
/// Gets or sets a value that determines whether the C code is parsed as a single header or multiple headers.
/// </summary>
/// <para>
/// Default is <c>true</c>. Use <c>true</c> to parse only top-level cursors which are externally visible. Use
/// <c>false</c> to parse all top-level cursors whether or not they are externally visible.
/// Default is <c>false</c>. Use <c>true</c> to parse the C code as a single header. Use <c>false</c> to parse
/// the C code as multiple headers.
/// </para>
[JsonPropertyName("isEnabledOnlyExternalTopLevelCursors")]
public bool? IsEnabledOnlyExternalTopLevelCursors { get; set; }
[JsonPropertyName("isSingleHeader")]
public bool? IsSingleHeader { get; set; }

/// <summary>
/// Gets or sets the cursor names to be treated as opaque types.
Expand Down Expand Up @@ -153,4 +113,10 @@ public sealed class UnsanitizedExtractInput : ToolUnsanitizedInput
/// </summary>
[JsonPropertyName("appleFrameworks")]
public ImmutableArray<string>? AppleFrameworks { get; set; }

/// <summary>
/// Gets or sets the name of enums that are explicitly allowed.
/// </summary>
[JsonPropertyName("includedNames")]
public ImmutableArray<string>? IncludedNames { get; set; }
}
8 changes: 8 additions & 0 deletions src/cs/production/c2ffi.Tool/c2ffi.Tool.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,13 @@
<Folder Include="Commands\Extract\Infrastructure\" />
<Folder Include="Generated\" />
</ItemGroup>

<!-- libclang -->
<ItemGroup>
<None Include="$(GitRepositoryPath)\lib\libclang.dll" Condition="Exists('$(GitRepositoryPath)\lib\libclang.dll')">
<Link>libclang.dll</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Loading