Skip to content
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: 2 additions & 1 deletion binding/SkiaSharp/SKStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ public bool Seek (int position)
return SkiaApi.sk_stream_seek (Handle, (IntPtr)position);
}

public bool Move (long offset) => Move ((int)offset);
[Obsolete ("The native stream move offset is capped at a 32-bit int. Use Move(int) instead.")]
public bool Move (long offset) => Move (checked ((int)offset));

public bool Move (int offset)
{
Expand Down
4 changes: 2 additions & 2 deletions binding/SkiaSharp/SkiaApi.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14397,7 +14397,7 @@ internal static bool sk_stream_is_at_end (sk_stream_t cstream) =>
(sk_stream_is_at_end_delegate ??= GetSymbol<Delegates.sk_stream_is_at_end> ("sk_stream_is_at_end")).Invoke (cstream);
#endif

// bool sk_stream_move(sk_stream_t* cstream, int offset)
// bool sk_stream_move(sk_stream_t* cstream, int32_t offset)
#if !USE_DELEGATES
#if USE_LIBRARY_IMPORT
[LibraryImport (SKIA)]
Expand Down Expand Up @@ -17533,7 +17533,7 @@ namespace SkiaSharp {
[return: MarshalAs (UnmanagedType.I1)]
internal unsafe delegate bool SKManagedStreamIsAtEndProxyDelegate(sk_stream_managedstream_t s, void* context);

// typedef bool (*)(sk_stream_managedstream_t* s, void* context, int offset)* sk_managedstream_move_proc
// typedef bool (*)(sk_stream_managedstream_t* s, void* context, int32_t offset)* sk_managedstream_move_proc
[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
[return: MarshalAs (UnmanagedType.I1)]
internal unsafe delegate bool SKManagedStreamMoveProxyDelegate(sk_stream_managedstream_t s, void* context, Int32 offset);
Expand Down
74 changes: 66 additions & 8 deletions utils/SkiaSharpGenerator/BaseTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,39 @@ protected void ParseSkiaHeaders()
}
}

if (OperatingSystem.IsLinux())
{
var process = new System.Diagnostics.Process
{
StartInfo = new System.Diagnostics.ProcessStartInfo
{
FileName = "clang",
Arguments = "-print-resource-dir",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
}
};
try
{
process.Start();
var resourceDir = process.StandardOutput.ReadToEnd().Trim();
process.WaitForExit();
if (!string.IsNullOrEmpty(resourceDir))
{
var include = Path.Combine(resourceDir, "include");
if (Directory.Exists(include))
options.SystemIncludeFolders.Add(include);
}
}
catch { }
foreach (var sys in new[] { "/usr/include/x86_64-linux-gnu", "/usr/include" })
{
if (Directory.Exists(sys))
options.SystemIncludeFolders.Add(sys);
}
}

foreach (var header in config.IncludeDirs)
{
var path = Path.Combine(SkiaRoot, header);
Expand Down Expand Up @@ -192,14 +225,16 @@ protected void LoadStandardMappings()
{ "signed int", nameof(Int32) },
{ "unsigned", nameof(UInt32) },
{ "unsigned int", nameof(UInt32) },
{ "long", nameof(Int64) },
{ "long int", nameof(Int64) },
// NOTE: C `long` / `unsigned long` / `signed long` (and the
// equivalent `long int` spellings) are intentionally omitted.
// Their size is platform-dependent (32-bit on Windows LLP64,
// 64-bit on Linux/macOS LP64), so no single managed type maps
// correctly on all platforms. GetType() below rejects them with
// a diagnostic pointing at int32_t / int64_t.
{ "long long", nameof(Int64) },
{ "long long int", nameof(Int64) },
{ "signed long", nameof(Int64) },
{ "signed long int", nameof(Int64) },
{ "unsigned long", nameof(UInt64) },
{ "unsigned long int", nameof(UInt64) },
{ "unsigned long long", nameof(UInt64) },
{ "unsigned long long int", nameof(UInt64) },
{ "float", nameof(Single) },
{ "double", nameof(Double) },
// TODO: long double, wchar_t ?
Expand Down Expand Up @@ -317,6 +352,16 @@ protected async Task<Config> LoadConfigAsync(string configPath)
return null;
}

// C types whose sizes differ between LLP64 (Windows) and LP64 (Linux/macOS)
// platforms. There is no single managed mapping that is correct on both,
// so we refuse to generate bindings for them instead of silently picking
// one and propagating the platform-dependent bug.
private static readonly HashSet<string> PlatformDependentLongTypes = new()
{
"long", "signed long", "unsigned long",
"long int", "signed long int", "unsigned long int",
};

protected string GetType(CppType type)
{
var typeName = GetCppType(type);
Expand All @@ -326,6 +371,15 @@ protected string GetType(CppType type)
var pointers = pointerIndex == -1 ? "" : typeName.Substring(pointerIndex);
var noPointers = pointerIndex == -1 ? typeName : typeName.Substring(0, pointerIndex);

if (PlatformDependentLongTypes.Contains(noPointers.TrimEnd()))
{
throw new InvalidOperationException(
$"C type '{noPointers.TrimEnd()}' has a platform-dependent size " +
$"(32-bit on Windows LLP64, 64-bit on Linux/macOS LP64). " +
$"Use int32_t or int64_t explicitly in the C header, " +
$"or add an explicit type override in the JSON config.");
}

if (skiaTypes.TryGetValue(noPointers, out var isStruct))
{
if (!isStruct)
Expand All @@ -352,8 +406,12 @@ protected static string GetCppType(CppType type)
{
var typeName = type.GetDisplayName();

// remove the const
typeName = typeName.Replace("const ", "");
// remove the const (both prefix and suffix forms — CppAst switched between them)
typeName = typeName.Replace("const ", "").Replace(" const", "");

// normalize whitespace around pointer/reference sigils: newer CppAst emits
// "T *" instead of "T*", which breaks the pointer/name split downstream
typeName = typeName.Replace(" *", "*").Replace(" &", "&");

// replace the [] with a *
int start;
Expand Down
18 changes: 15 additions & 3 deletions utils/SkiaSharpGenerator/Generate/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ private void WriteDelegate(TextWriter writer, CppTypedef del)
functionMappings.TryGetValue(name, out var map);
name = map?.CsType ?? CleanName(name);

writer.WriteLine($"\t// {del}");
writer.WriteLine($"\t// {FormatCppSignature(del.ToString())}");
writer.WriteLine($"\t[UnmanagedFunctionPointer (CallingConvention.Cdecl)]");

var (paramsList, returnType) = GetManagedFunctionArguments(function, map);
Expand Down Expand Up @@ -193,7 +193,7 @@ private void WriteStruct(TextWriter writer, CppClass klass)
var funcPointerType = GetFunctionPointerType(field.Type);
var cppT = GetCppType(field.Type);

writer.WriteLine($"\t\t// {field}");
writer.WriteLine($"\t\t// {FormatCppSignature(field.ToString())}");

var fieldName = field.Name;
var isPrivate = fieldName.StartsWith("_private_", StringComparison.OrdinalIgnoreCase);
Expand Down Expand Up @@ -540,7 +540,7 @@ private void WriteFunctions(TextWriter writer)
}

writer.WriteLine();
writer.WriteLine($"\t\t// {function}");
writer.WriteLine($"\t\t// {FormatCppSignature(function)}");
writer.WriteLine($"\t\t#if !USE_DELEGATES");
writer.WriteLine($"\t\t#if USE_LIBRARY_IMPORT");
writer.WriteLine($"\t\t[LibraryImport ({config.DllName})]");
Expand Down Expand Up @@ -654,6 +654,18 @@ private void WriteDelegateProxy(TextWriter writer, CppTypedef del)
}
}

// Normalize CppAst's ToString() output to the historical style the checked-in
// generated files use: "const T*" instead of "T const *", no space before *.
private static string FormatCppSignature(CppFunction function) =>
FormatCppSignature(function.ToString());

private static string FormatCppSignature(string s)
{
s = System.Text.RegularExpressions.Regex.Replace(s, @"(\w+)\s+const\b", "const $1");
s = s.Replace(" *", "*").Replace(" &", "&");
return s;
}

private void WriteDocIfAny(TextWriter writer, string key, string indent)
{
if (PreviousDocumentation?.TryGet(key, out var xml) != true)
Expand Down
2 changes: 1 addition & 1 deletion utils/SkiaSharpGenerator/SkiaSharpGenerator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CppAst" Version="0.13.0" />
<PackageReference Include="CppAst" Version="0.21.4" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.6.0" />
<PackageReference Include="Mono.Cecil" Version="0.11.2" />
<PackageReference Include="Mono.Options" Version="5.3.0.1" />
Expand Down
Loading