Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
25 changes: 18 additions & 7 deletions src/SharpMetal.Generator/HeaderInfo.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Diagnostics;
Comment thread
pavelkouril marked this conversation as resolved.
Outdated
using SharpMetal.Generator.Instances;
using SharpMetal.Generator.Utilities;

Expand All @@ -17,6 +18,7 @@
FilePath = filePath;
using var sr = new StreamReader(File.OpenRead(filePath));
var namespacePrefix = Namespaces.GetNamespace(filePath);
var macroNamespacePrefix = Namespaces.GetMacroNamespace(namespacePrefix);
var inMtlPrivateDefSel = false;

while (!sr.EndOfStream)
Expand Down Expand Up @@ -100,12 +102,20 @@
{
if (!line.Contains(';'))
{
var classInstance = ClassInstance.Build(line, namespacePrefix, sr, InFlightUnscopedMethods);
if (InFlightUnscopedMethods.Count > 0)
{
Console.WriteLine($"Unscoped methods found in header {filePath}:");
foreach (var unscopedMethod in InFlightUnscopedMethods)
{
Console.WriteLine($"- {unscopedMethod.Name}");
}
InFlightUnscopedMethods.Clear();
}
var classInstance = ClassInstance.Build(line, namespacePrefix, sr);
if (classInstance.IsValid && !GeneratorUtils.IsBannedType(classInstance.Name))
{
ClassInstances.Add(classInstance);
}
InFlightUnscopedMethods.Clear();
}
}
else if (line.StartsWith("struct"))
Expand All @@ -115,12 +125,12 @@
StructInstances.Add(StructInstance.Build(line, namespacePrefix, sr));
}
}
else if (line.StartsWith($"_{namespacePrefix}_ENUM") || line.StartsWith($"_{namespacePrefix}_OPTIONS"))
else if (line.StartsWith($"_{macroNamespacePrefix}_ENUM") || line.StartsWith($"_{macroNamespacePrefix}_OPTIONS"))
{
EnumInstances.Add(EnumInstance.Build(line, namespacePrefix, sr));
}
// These contain all the selectors we need
else if (line.StartsWith($"_{namespacePrefix}_INLINE"))
else if (line.StartsWith($"_{macroNamespacePrefix}_INLINE"))
{
SelectorInstance.Build(line, namespacePrefix, sr, ClassInstances);
}
Expand Down Expand Up @@ -161,7 +171,7 @@
if (line.Contains("()"))
{
// Function has no arguments
method = new MethodInstance(returnType, name, rawName, true, true, []);
method = new MethodInstance(returnType, name, rawName, true, false, []);
}
else
{
Expand Down Expand Up @@ -211,12 +221,13 @@
argumentName = "mtlEvent";
}

arguments.Add(new PropertyInstance(argumentType, argumentName));
arguments.Add(new PropertyInstance(null, argumentType, argumentName));

Check warning on line 224 in src/SharpMetal.Generator/HeaderInfo.cs

View workflow job for this annotation

GitHub Actions / build / build

Cannot convert null literal to non-nullable reference type.

Check warning on line 224 in src/SharpMetal.Generator/HeaderInfo.cs

View workflow job for this annotation

GitHub Actions / build / build

Cannot convert null literal to non-nullable reference type.

Check warning on line 224 in src/SharpMetal.Generator/HeaderInfo.cs

View workflow job for this annotation

GitHub Actions / build / build

Cannot convert null literal to non-nullable reference type.
}

if (returnType != string.Empty)
{
method = new MethodInstance(returnType, name, rawName, true, true, arguments);
// This is just for recording the unscoped method, no actual real method goes through this codepath
method = new MethodInstance(returnType, name, rawName, true, false, arguments);
}
}

Expand Down
100 changes: 61 additions & 39 deletions src/SharpMetal.Generator/Instances/ClassInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,14 @@

public List<ObjectiveCInstance> Generate(List<ClassInstance> classCache, List<EnumInstance> enumCache, List<StructInstance> structCache, CodeGenContext context)
{
if (_parent != string.Empty)
// Make copies since we will modify these by adding the hiearchy
// This is not ideal, but is the simplest way to make it independent on the processing order
var propertyInstances = new List<PropertyInstance>(_propertyInstances);
var methodInstances = new List<MethodInstance>(_methodInstances);
var selectorInstances = new List<SelectorInstance>(_selectorInstances);

var parentName = _parent;
while (parentName != string.Empty)
{
// To properly fit within expected C# patterns, we
// should generate an interface to hold the associated
Expand All @@ -67,18 +74,31 @@
// using classes, however that comes with performance
// and memory drawbacks, that structs are able to avoid.

var parent = classCache.FirstOrDefault(x => x.Name == _parent);

var parent = classCache.FirstOrDefault(x => x.Name == parentName);
parentName = parent?._parent ?? string.Empty;
if (parent != null)
{
_propertyInstances.AddRange(parent._propertyInstances);
_methodInstances.AddRange(parent._methodInstances);
_selectorInstances.AddRange(parent._selectorInstances);
// Just add unique instances, since the MTLAllocation inheritance would cause doubled-up properties
foreach (var property in parent._propertyInstances)
{
if (!propertyInstances.Any(x => x.Name == property.Name && x.Type == property.Type))
{
propertyInstances.Add(property);
}
}
methodInstances.AddRange(parent._methodInstances);
foreach (var selector in parent._selectorInstances)
{
if (selectorInstances.All(x => x.Name != selector.Name))
{
selectorInstances.Add(selector);
}
}
}
}

_propertyInstances.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.InvariantCultureIgnoreCase));
_methodInstances.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.InvariantCultureIgnoreCase));
propertyInstances.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.InvariantCultureIgnoreCase));
methodInstances.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.InvariantCultureIgnoreCase));

var objectiveCInstances = new List<ObjectiveCInstance>();

Expand Down Expand Up @@ -120,36 +140,36 @@

context.LeaveScope();

if (_propertyInstances.Count != 0)
if (propertyInstances.Count != 0)
{
context.WriteLine();
}

var selectorInstances = new List<SelectorInstance>(_selectorInstances);
var unsortedSelectorInstances = new List<SelectorInstance>(selectorInstances);
// These have to be sorted after making the copy, otherwise it might resolve to wrong selector due to the Find-based mechanism when generating the calls
_selectorInstances.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.InvariantCultureIgnoreCase));
selectorInstances.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.InvariantCultureIgnoreCase));

for (var j = 0; j < _propertyInstances.Count; j++)
for (var j = 0; j < propertyInstances.Count; j++)
{
objectiveCInstances.Add(_propertyInstances[j].Generate(selectorInstances, enumCache, structCache, context));
objectiveCInstances.Add(propertyInstances[j].Generate(unsortedSelectorInstances, enumCache, structCache, context));

if (j != _propertyInstances.Count - 1)
if (j != propertyInstances.Count - 1)
{
context.WriteLine();
}
}

foreach (var method in _methodInstances)
foreach (var method in methodInstances)
{
objectiveCInstances.Add(method.Generate(selectorInstances, enumCache, structCache, context, _namespacePrefix));
objectiveCInstances.Add(method.Generate(unsortedSelectorInstances, enumCache, structCache, context, _namespacePrefix));
}

if (_selectorInstances.Count != 0)
if (selectorInstances.Count != 0)
{
context.WriteLine();
}

foreach (var selector in _selectorInstances)
foreach (var selector in selectorInstances)
{
context.WriteLine($"private static readonly Selector {selector.Name} = \"{selector.Selector}\";");
}
Expand All @@ -160,7 +180,7 @@
return objectiveCInstances;
}

public static ClassInstance Build(string line, string namespacePrefix, StreamReader sr, List<MethodInstance> inFlightUnscopedMethods)
public static ClassInstance Build(string line, string namespacePrefix, StreamReader sr)
{
var classInfo = line.Split(" ", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);

Expand All @@ -182,25 +202,22 @@
var info = ancestorInfo[index..];
info = info.Replace(">", "").Replace("<", "");
var ancestors = info.Split(",", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
for (var i = 0; i < ancestors.Length; i++)
for (var i = 1; i < ancestors.Length; i++)
{
if (i > 0)
{
if (ancestors[i] != "_Base" && ancestors[i] != "Type" && ancestors[i] != "objc_object" &&
if (ancestors[i] != "_Base" && ancestors[i] != "Type" && ancestors[i] != "objc_object" &&
ancestors[i] != "Value")
{
instance._parent = Types.ConvertType(ancestors[i], namespacePrefix);
}
{
instance._parent = Types.ConvertType(ancestors[i], namespacePrefix);
}
}
}
}

instance._methodInstances.AddRange(inFlightUnscopedMethods);
instance._namespacePrefix = namespacePrefix;

var classEnded = false;
var enteredComment = false;
var isDeprecated = false;

while (!classEnded)
{
Expand Down Expand Up @@ -232,6 +249,12 @@
continue;
}

if (nextLine.Contains("[[deprecated"))
{
isDeprecated = true;
continue;
}

// Ignore empty lines etc...
if (nextLine is "" or "{" or "public:")
{
Expand All @@ -243,6 +266,10 @@
continue;
}

// Temp to not having to track all early returns
var tempDeprecated = isDeprecated;
isDeprecated = false;

var rawName = nextLine;
nextLine = StringUtils.FunctionSignatureCleanup(nextLine);

Expand Down Expand Up @@ -293,17 +320,17 @@
continue;
}

if (returnType == "void" || returnType == string.Empty || isStatic)
if (returnType == "void" || returnType == string.Empty)
{
// This is probably a constructor, in which case we have our own implementation
if (!returnType.Contains(name))
{
instance.AddMethod(new MethodInstance(returnType, name, rawName, isStatic, false, new List<PropertyInstance>()));
instance.AddMethod(new MethodInstance(returnType, name, rawName, isStatic, tempDeprecated, new List<PropertyInstance>()));
}
}
else
else if (!(isStatic && returnType.Contains(name)))
{
instance.AddProperty(new PropertyInstance(returnType, name));
instance.AddProperty(new PropertyInstance(instance, returnType, name, isStatic: isStatic, isDeprecated: tempDeprecated));
}
}
else
Expand Down Expand Up @@ -360,12 +387,12 @@
reference = true;
}

arguments.Add(new PropertyInstance(argumentType, argumentName, reference));
arguments.Add(new PropertyInstance(null, argumentType, argumentName, reference));

Check warning on line 390 in src/SharpMetal.Generator/Instances/ClassInstance.cs

View workflow job for this annotation

GitHub Actions / build / build

Cannot convert null literal to non-nullable reference type.

Check warning on line 390 in src/SharpMetal.Generator/Instances/ClassInstance.cs

View workflow job for this annotation

GitHub Actions / build / build

Cannot convert null literal to non-nullable reference type.
}

if (returnType != string.Empty)
{
instance.AddMethod(new MethodInstance(returnType, name, rawName, isStatic, false, arguments));
instance.AddMethod(new MethodInstance(returnType, name, rawName, isStatic, tempDeprecated, arguments));
}
}

Expand All @@ -377,17 +404,12 @@
// We can't have a property AND methods with the same name
// in this case, the solution is to turn the property into a method
instance._propertyInstances.RemoveAt(i);
instance.AddMethod(new MethodInstance(property.Type, property.Name, rawName, isStatic, false, []));
instance.AddMethod(new MethodInstance(property.Type, property.Name, rawName, isStatic, tempDeprecated, []));
}
}
}

return instance;
}

public List<SelectorInstance> GetSelectors()
{
return _selectorInstances;
}
}
}
6 changes: 4 additions & 2 deletions src/SharpMetal.Generator/Instances/EnumInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ public static EnumInstance Build(string line, string namespacePrefix, StreamRead
{
var isFlag = line.Contains($"_{namespacePrefix}_OPTIONS(");

line = line.Replace($"_{namespacePrefix}_ENUM(", "");
line = line.Replace($"_{namespacePrefix}_OPTIONS(", "");
var macroNamespaces = Namespaces.GetMacroNamespace(namespacePrefix);

line = line.Replace($"_{macroNamespaces}_ENUM(", "");
line = line.Replace($"_{macroNamespaces}_OPTIONS(", "");

if (line.Contains('{'))
{
Expand Down
28 changes: 6 additions & 22 deletions src/SharpMetal.Generator/Instances/MethodInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ public class MethodInstance

private readonly string _returnType;
private readonly bool _isStatic;
private readonly bool _unscoped;
private readonly string _rawName;
private readonly bool _isDeprecated;

public MethodInstance(string returnType, string name, string rawName, bool isStatic, bool unscoped, List<PropertyInstance> inputInstances)
public MethodInstance(string returnType, string name, string rawName, bool isStatic, bool isDeprecated, List<PropertyInstance> inputInstances)
{
Name = name;
InputInstances = inputInstances;

_returnType = returnType;
_isStatic = isStatic;
_unscoped = unscoped;
_isDeprecated = isDeprecated;

var rawNameComponents = rawName.Split(" ", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
for (var index = 0; index < rawNameComponents.Length; index++)
Expand Down Expand Up @@ -45,26 +45,10 @@ public MethodInstance(string returnType, string name, string rawName, bool isSta

public ObjectiveCInstance Generate(List<SelectorInstance> selectorInstances, List<EnumInstance> enumCache, List<StructInstance> structCache, CodeGenContext context, string namespacePrefix, bool prependSpace = true)
{
var selector = selectorInstances.Find(x => x.RawName.ToLower().Replace(" class ", " ").Replace("mtl::", "").Replace("ns::", "").Contains(_rawName.ToLower()));
// Disallow the selectors used in properties to prevent doubling of properties and methods that do the same
var selector = selectorInstances.Find(x => !x.UsedInProperty && x.RawName.ToLower().Replace(" class ", " ").Replace("mtl::", "").Replace("ns::", "").Contains(_rawName.ToLower()));

if (selector == null)
{
if (_rawName != "lock()" && _rawName != "unlock()" && _rawName != "release()" && _unscoped)
{
if (prependSpace)
{
context.WriteLine();
}
context.WriteLine("[LibraryImport(ObjectiveC.MetalFramework)]");
context.WriteLine($"private static partial IntPtr {namespacePrefix}{_rawName};");
context.WriteLine();
context.WriteLine($"public static {_returnType} {_rawName}");
context.EnterScope();
context.WriteLine($"return new({namespacePrefix}{_rawName});");
context.LeaveScope();
}
}
else
if (selector != null)
{
var staticString = _isStatic ? "static " : "";
// TODO: Handle array inputs
Expand Down
2 changes: 2 additions & 0 deletions src/SharpMetal.Generator/Instances/ObjectiveCInstance.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using SharpMetal.Generator.Utilities;
Comment thread
pavelkouril marked this conversation as resolved.
Outdated

namespace SharpMetal.Generator.Instances
{
public class ObjectiveCInstance : IEquatable<ObjectiveCInstance>, IComparable<ObjectiveCInstance>
Expand Down
Loading
Loading