Skip to content

Commit

Permalink
Add ability to explicitly include an enum by name
Browse files Browse the repository at this point in the history
  • Loading branch information
lithiumtoast committed Aug 28, 2024
1 parent 7c7f543 commit 609e5cf
Show file tree
Hide file tree
Showing 13 changed files with 245 additions and 57 deletions.
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 @@ -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 @@ -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; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) Bottlenose Labs Inc. (https://github.com/bottlenoselabs). All rights reserved.
// Licensed under the MIT license. See LICENSE file in the Git repository root directory for full license information.

#pragma warning disable CA1707

namespace c2ffi.Tests.EndToEnd.Extract.Functions.function_implicit_enum;

public class Test : ExtractFfiTest
{
private const string FunctionName = "function_implicit_enum";

[Fact]
public void Function()
{
var ffis = GetTargetPlatformFfis(
$"src/c/tests/functions/{FunctionName}/config.json");
Assert.True(ffis.Length > 0);

foreach (var ffi in ffis)
{
FfiFunctionExists(ffi);
FfiEnumExists(ffi);
}
}

private void FfiFunctionExists(CTestFfiTargetPlatform ffi)
{
var function = ffi.GetFunction(FunctionName);
function.CallingConvention.Should().Be("cdecl");

var returnType = function.ReturnType;
returnType.Name.Should().Be("int");
returnType.NodeKind.Should().Be("primitive");
returnType.SizeOf.Should().Be(4);
returnType.AlignOf.Should().Be(4);
returnType.InnerType.Should().BeNull();

function.Parameters.Should().HaveCount(1);
var parameter = function.Parameters[0];
parameter.Name.Should().Be("value");
parameter.Type.Name.Should().Be("int");
parameter.Type.NodeKind.Should().Be("primitive");
parameter.Type.SizeOf.Should().Be(4);
parameter.Type.AlignOf.Should().Be(4);
}

private void FfiEnumExists(CTestFfiTargetPlatform ffi)
{
var @enum = ffi.GetEnum("enum_implicit");
@enum.Values.Should().HaveCount(2);
@enum.SizeOf.Should().Be(4);
}
}
Loading

0 comments on commit 609e5cf

Please sign in to comment.