in SharpGen.Platform/CppParser.cs [320:450]
private static void ParseAnnotations(XElement xElement, CppElement cppElement)
{
// Check that the xml contains the "attributes" attribute
var attributes = xElement.AttributeValue("attributes");
if (string.IsNullOrWhiteSpace(attributes))
return;
// Strip whitespaces inside annotate("...")
var stripSpaces = new StringBuilder();
var doubleQuoteCount = 0;
for (var i = 0; i < attributes.Length; i++)
{
var addThisChar = true;
var attributeChar = attributes[i];
if (attributeChar == '(')
{
doubleQuoteCount++;
}
else if (attributeChar == ')')
{
doubleQuoteCount--;
}
else if (doubleQuoteCount > 0 && (char.IsWhiteSpace(attributeChar) | attributeChar == '"'))
{
addThisChar = false;
}
if (addThisChar)
stripSpaces.Append(attributeChar);
}
attributes = stripSpaces.ToString();
// Default calling convention
var cppCallingConvention = CppCallingConvention.Unknown;
// Default parameter attribute
var paramAttribute = ParamAttribute.None;
// Default Guid
string guid = null;
// Parse attributes
const string gccXmlAttribute = "annotate(";
var isPost = false;
var hasWritable = false;
// Clang outputs attributes in reverse order
// TODO: Check if applies to all declarations
foreach (var item in attributes.Split(' ').Reverse())
{
var newItem = item;
if (newItem.StartsWith(gccXmlAttribute))
newItem = newItem.Substring(gccXmlAttribute.Length);
if (newItem.StartsWith("SAL_pre"))
{
isPost = false;
}
else if (newItem.StartsWith("SAL_post"))
{
isPost = true;
}
else if (isPost && newItem.StartsWith("SAL_valid"))
{
paramAttribute |= ParamAttribute.Out;
}
else if (newItem.StartsWith("SAL_maybenull") || (newItem.StartsWith("SAL_null") && newItem.Contains("__maybe")))
{
paramAttribute |= ParamAttribute.Optional;
}
else if (newItem.StartsWith("SAL_readableTo") || newItem.StartsWith("SAL_writableTo"))
{
if (newItem.StartsWith("SAL_writableTo"))
{
// When changing something related to in/out SAL, check resulting attributes
// for IInspectable::GetIids::iids (_Outptr_result_buffer_to_maybenull_)
// Should be Out|Buffer|Optional
if (!isPost) paramAttribute |= ParamAttribute.Out;
hasWritable = true;
}
if (!newItem.Contains("SPECSTRINGIZE(1)") && !newItem.Contains("elementCount(1)"))
paramAttribute |= ParamAttribute.Buffer;
}
else if (newItem.StartsWith("__stdcall__"))
{
cppCallingConvention = CppCallingConvention.StdCall;
}
else if (newItem.StartsWith("__cdecl__"))
{
cppCallingConvention = CppCallingConvention.CDecl;
}
else if (newItem.StartsWith("__thiscall__"))
{
cppCallingConvention = CppCallingConvention.ThisCall;
}
else if (newItem.StartsWith("uuid("))
{
guid = newItem.Trim(')').Substring("uuid(".Length).Trim('"', '{', '}');
}
}
// If no writable, than this is an In parameter
if (!hasWritable)
{
paramAttribute |= ParamAttribute.In;
}
// Update CppElement based on its type
if (cppElement is CppParameter param)
{
// Replace in & out with inout.
// Todo check to use in & out instead of inout
if ((paramAttribute & ParamAttribute.In) != 0 && (paramAttribute & ParamAttribute.Out) != 0)
{
paramAttribute ^= ParamAttribute.In;
paramAttribute ^= ParamAttribute.Out;
paramAttribute |= ParamAttribute.InOut;
}
param.Attribute = paramAttribute;
}
else if (cppElement is CppCallable callable && cppCallingConvention != CppCallingConvention.Unknown)
{
callable.CallingConvention = cppCallingConvention;
}
else if (cppElement is CppInterface iface && guid != null)
{
iface.Guid = guid;
}
}