public static SalAttribute CreateFromCppAttribute(string attr) { SalAttribute ret = new SalAttribute(); var parts = attr.Split(';'); foreach (var part in parts) { var nameAndValue = part.Split('='); var name = nameAndValue[0].Trim(); var value = nameAndValue[1].Replace("\\\"", string.Empty); switch (name) { case "Name": ret.Name = value; break; case "p1": ret.P1 = value; break; case "p2": ret.P2 = value; break; case "p3": ret.P3 = value; break; } } return(ret); }
private SyntaxNode CreateAttributeListForSal(AttributeListSyntax cppAttrList) { ParameterSyntax paramNode = (ParameterSyntax)cppAttrList.Parent; bool marshalAsAdded = this.nodesWithMarshalAs.Contains(paramNode); AttributeSyntax cppAttr = cppAttrList.Attributes[0]; List <AttributeSyntax> attributesList = new List <AttributeSyntax>(); string salText = cppAttr.ArgumentList.Arguments[0].ToString(); salText = salText.Substring(1, salText.Length - 2); string nativeArrayInfoParams = null; bool isIn = false; bool isOut = false; bool isOpt = false; bool isComOutPtr = false; bool isRetVal = false; bool isNullNullTerminated; bool? pre = null; bool? post = null; var salAttrs = GetSalAttributes(salText); isNullNullTerminated = salAttrs.Any(a => a.Name == "SAL_name" && a.P1 == "_NullNull_terminated_"); foreach (var salAttr in salAttrs) { if (salAttr.Name == "SAL_name") { if (salAttr.P1.StartsWith("_COM_Outptr")) { isComOutPtr = true; continue; } else if (salAttr.P1.StartsWith("_Outptr_") && !isComOutPtr) { isOut = true; continue; } else if (salAttr.P1.StartsWith("__RPC__")) { // TODO: Handle ecount, xcount and others that deal with counts string[] parts = salAttr.P1.Split('_'); foreach (var part in parts) { switch (part) { case "in": isIn = true; break; case "out": isOut = true; break; case "inout": isIn = isOut = true; break; case "opt": isOpt = true; break; } } break; } } if (salAttr.Name == "SAL_null" && salAttr.P1 == "__maybe") { isOpt = true; continue; } if (salAttr.Name == "SAL_retval") { isRetVal = true; continue; } if (salAttr.Name == "SAL_pre") { pre = true; continue; } if (salAttr.Name == "SAL_post") { pre = false; post = true; continue; } if (salAttr.Name == "SAL_end") { pre = post = false; } if (salAttr.Name == "SAL_valid") { if (pre.HasValue && pre.Value) { isIn = true; } else if (post.HasValue && post.Value) { isOut = true; } else { isIn = isOut = true; } continue; } if (salAttr.Name == "SAL_name" && salAttr.P1 == "_Post_valid_") { isOut = true; continue; } if (!marshalAsAdded && (salAttr.Name == "SAL_writableTo" || salAttr.Name == "SAL_readableTo") && pre.HasValue && pre.Value) { nativeArrayInfoParams = GetArrayMarshalAsFromP1(paramNode, salAttr.P1); if (!string.IsNullOrEmpty(nativeArrayInfoParams)) { marshalAsAdded = true; } continue; } } // If we didn't add marshal as yet, try again without using pre if (!marshalAsAdded) { var salAttr = salAttrs.FirstOrDefault(attr => attr.Name == "SAL_readableTo" || attr.Name == "SAL_writeableTo"); if (salAttr != null) { nativeArrayInfoParams = GetArrayMarshalAsFromP1(paramNode, salAttr.P1); if (!string.IsNullOrEmpty(nativeArrayInfoParams)) { marshalAsAdded = true; } } } if (!string.IsNullOrEmpty(nativeArrayInfoParams)) { var attrName = SyntaxFactory.ParseName("NativeArrayInfo"); var args = SyntaxFactory.ParseAttributeArgumentList(nativeArrayInfoParams.ToString()); var finalAttr = SyntaxFactory.Attribute(attrName, args); attributesList.Add(finalAttr); } if (isIn) { attributesList.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName("In"))); } if (isComOutPtr) { attributesList.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName("ComOutPtr"))); } else if (isOut) { attributesList.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName("Out"))); } if (isOpt) { attributesList.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName("Optional"))); } if (isNullNullTerminated) { attributesList.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName("NullNullTerminated"))); } if (isRetVal) { attributesList.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName("RetVal"))); } if (attributesList.Count == 0) { return(null); } return(SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(attributesList))); string GetArrayMarshalAsFromP1(ParameterSyntax paramNode, string p1Text) { ParameterListSyntax parameterListNode = (ParameterListSyntax)paramNode.Parent; var match = elementCountRegex.Match(p1Text); StringBuilder ret = new StringBuilder("("); if (match.Success) { string sizeOrParamName = match.Groups[1].Value; if (int.TryParse(sizeOrParamName, out int size)) { // Don't bother marking this as an array if it only has 1 if (size == 1) { return(string.Empty); } if (ret.Length != 1) { ret.Append(", "); } ret.Append($"SizeConst = {size}"); } else { sizeOrParamName = sizeOrParamName.Replace("*", string.Empty); for (int i = 0; i < parameterListNode.Parameters.Count; i++) { if (parameterListNode.Parameters[i].Identifier.ValueText == sizeOrParamName) { if (ret.Length != 1) { ret.Append(", "); } string propName = p1Text.StartsWith("elementCount") ? "SizeParamIndex" : "BytesParamIndex"; ret.Append($"{propName} = {i}"); break; } } } } else { // If it didn't match the regex and we don't see inexpressibleCount, we can't do // anything but return an empty string, because we don't know how to interpret it if (!p1Text.StartsWith("inexpressibleCount")) { ret = new StringBuilder(); } } if (ret.Length > 1) { ret.Append(')'); return(ret.ToString()); } return(string.Empty); } IEnumerable <SalAttribute> GetSalAttributes(string salArgsText) { foreach (var attr in salArgsText.Split('^')) { var salAttr = SalAttribute.CreateFromCppAttribute(attr); if (salAttr != null) { yield return(salAttr); } } } }
private SyntaxNode CreateAttributeListForSal(AttributeListSyntax cppAttrList) { ParameterSyntax paramNode = (ParameterSyntax)cppAttrList.Parent; bool marshalAsAdded = this.nodesWithMarshalAs.Contains(paramNode); AttributeSyntax cppAttr = cppAttrList.Attributes[0]; List <AttributeSyntax> attributesList = new List <AttributeSyntax>(); string salText = cppAttr.ArgumentList.Arguments[0].ToString(); salText = salText.Substring(1, salText.Length - 2); string marshalAsParams = null; bool isIn = false; bool isOut = false; bool isOpt = false; bool isComOutPtr = false; bool isNullNullTerminated; bool? pre = null; bool? post = null; var salAttrs = GetSalAttributes(salText); isNullNullTerminated = salAttrs.Any(a => a.Name == "SAL_name" && a.P1 == "_NullNull_terminated_"); foreach (var salAttr in salAttrs) { if (salAttr.Name == "SAL_name" && salAttr.P1.StartsWith("_COM_Outptr")) { isComOutPtr = true; continue; } if (salAttr.Name == "SAL_null" && salAttr.P1 == "__maybe") { isOpt = true; continue; } if (salAttr.Name == "SAL_pre") { pre = true; continue; } if (salAttr.Name == "SAL_post") { pre = false; post = true; continue; } if (salAttr.Name == "SAL_end") { pre = post = false; } if (salAttr.Name == "SAL_valid") { if (pre.HasValue && pre.Value) { isIn = true; } else if (post.HasValue && post.Value) { isOut = true; } else { isIn = isOut = true; } continue; } if (salAttr.Name == "SAL_name" && salAttr.P1 == "_Post_valid_") { isOut = true; continue; } if (!marshalAsAdded && (salAttr.Name == "SAL_writableTo" || salAttr.Name == "SAL_readableTo") && pre.HasValue && pre.Value) { marshalAsParams = GetArrayMarshalAsFromP1(paramNode, salAttr.P1, isNullNullTerminated); if (!string.IsNullOrEmpty(marshalAsParams)) { marshalAsAdded = true; } continue; } } // If we didn't add marshal as yet, try again without using pre if (!marshalAsAdded) { var salAttr = salAttrs.FirstOrDefault(attr => attr.Name == "SAL_readableTo" || attr.Name == "SAL_writeableTo"); if (salAttr != null) { marshalAsParams = GetArrayMarshalAsFromP1(paramNode, salAttr.P1, isNullNullTerminated); if (!string.IsNullOrEmpty(marshalAsParams)) { marshalAsAdded = true; } } } if (!string.IsNullOrEmpty(marshalAsParams)) { var attrName = SyntaxFactory.ParseName("NativeTypeInfo"); var args = SyntaxFactory.ParseAttributeArgumentList(marshalAsParams.ToString()); var finalAttr = SyntaxFactory.Attribute(attrName, args); attributesList.Add(finalAttr); } if (isIn) { attributesList.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName("In"))); } if (isComOutPtr) { attributesList.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName("ComOutPtr"))); } else if (isOut) { attributesList.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName("Out"))); } if (isOpt) { attributesList.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName("Optional"))); } if (attributesList.Count == 0) { return(null); } return(SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(attributesList))); string GetArrayMarshalAsFromP1(ParameterSyntax paramNode, string p1Text, bool isNullNullTerminated) { ParameterListSyntax parameterListNode = (ParameterListSyntax)paramNode.Parent; var match = elementCountRegex.Match(p1Text); string arraySubType = GetParameterArraySubtype(paramNode); StringBuilder ret = new StringBuilder("(UnmanagedType.LPArray"); if (arraySubType != null) { ret.Append($", ArraySubType = UnmanagedType.{arraySubType}"); } if (isNullNullTerminated) { ret.Append(", IsNullNullTerminated = true"); } if (match.Success) { string sizeOrParamName = match.Groups[1].Value; if (int.TryParse(sizeOrParamName, out int size)) { // Don't bother marking this as an array if it only has 1 if (size == 1) { return(string.Empty); } ret.Append($", SizeConst = {size}"); } else { sizeOrParamName = sizeOrParamName.Replace("*", string.Empty); for (int i = 0; i < parameterListNode.Parameters.Count; i++) { if (parameterListNode.Parameters[i].Identifier.ValueText == sizeOrParamName) { ret.Append($", SizeParamIndex = {i}"); break; } } } } else { // If it didn't match the regex and we don't see inexpressibleCount, we can't do // anything but return an empty string, because we don't know how to interpret it if (!p1Text.StartsWith("inexpressibleCount")) { ret = new StringBuilder(); } } if (ret.Length != 0) { ret.Append(')'); } return(ret.ToString()); string GetParameterArraySubtype(ParameterSyntax paramNode) { foreach (var attrList in paramNode.AttributeLists) { foreach (var attr in attrList.Attributes) { if (attr.Name.ToString() == "NativeTypeName") { string nativeType = attr.ArgumentList.Arguments[0].ToString(); nativeType = EncodeHelpers.RemoveQuotes(nativeType); if (nativeType.StartsWith("const ")) { nativeType = nativeType.Substring("const ".Length); } if (nativeType.EndsWith(" *")) { nativeType = nativeType.Substring(0, nativeType.Length - 2); } string arraySubType = ConvertTypeToMarshalAsType(nativeType, out _, out _, out _); return(arraySubType); } } } return(null); } } IEnumerable <SalAttribute> GetSalAttributes(string salArgsText) { foreach (var attr in salArgsText.Split('^')) { var salAttr = SalAttribute.CreateFromCppAttribute(attr); if (salAttr != null) { yield return(salAttr); } } } }