Skip to content

Make ComPtr results optional #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 20, 2018
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
4 changes: 2 additions & 2 deletions Generator/Types/ClassDef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ public override void CollectDependencies()

foreach (var m in factoryMethods)
{
methodWrappers.Add(new ClassMethodDef(m, this));
methodWrappers.Add(new ClassMethodDef(m, this, true));
}
foreach (var m in staticMethods)
{
methodWrappers.Add(new ClassMethodDef(m, this));
methodWrappers.Add(new ClassMethodDef(m, this, false));
}

// fix name clashes in method wrappers (caused by overloads from different interfaces)
Expand Down
8 changes: 4 additions & 4 deletions Generator/Types/ClassMethodDef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ public IEnumerable<TypeDef> ForeignAssemblyDependencies
}
}

public ClassMethodDef(MethodDef method, ClassDef containingClass)
public ClassMethodDef(MethodDef method, ClassDef containingClass, bool isFactory)
{
WrappedMethod = method;
ContainingClass = containingClass;
Name = WrappedMethod.Details.WrappedName;

AddDependency(method.DeclaringType);
inputParameters = WrappedMethod.Details.MakeInputParameters(Generator, this);
outType = WrappedMethod.Details.MakeOutType(Generator, this);
outType = WrappedMethod.Details.MakeOutType(Generator, this, isFactory);
}

public void FixupName(string suffix)
Expand All @@ -76,9 +76,9 @@ public string Emit()
var dependsOnAssemblies = new List<string>(ForeignAssemblyDependencies.GroupBy(t => t.Module.Assembly.Name.Name).Select(g => g.Key));
var features = new FeatureConditions(dependsOnAssemblies);

return $@"{ features.GetAttribute() }#[inline] pub fn { Name }({ String.Join(", ", inputParameters) }) -> Result<{ outType }> {{ unsafe {{
return $@"{ features.GetAttribute() }#[inline] pub fn { Name }({ String.Join(", ", inputParameters) }) -> Result<{ outType }> {{
<Self as RtActivatable<{ m.DeclaringType.Name }>>::get_activation_factory().{ m.Details.WrappedName }({ String.Join(", ", m.Details.InputParameterNames) })
}}}}";
}}";
}

public void AddDependency(TypeDef other)
Expand Down
23 changes: 18 additions & 5 deletions Generator/Types/InterfaceDef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@

namespace Generator.Types
{
public enum InterfaceKind
{
Unidentified,
Factory,
Statics,
Instance
}

public class InterfaceDef : TypeDef
{
public override TypeKind Kind
Expand All @@ -18,8 +26,8 @@ public override TypeKind Kind
}

public bool IsDelegate { get; private set; }
public InterfaceKind InterfaceKind { get; private set; }

private bool isFactoryOrStatic;
private List<MethodDef> methods;
public override IEnumerable<MethodDef> Methods
{
Expand Down Expand Up @@ -49,7 +57,7 @@ public override void CollectDependencies()
exclusiveToType = exclusiveTo.ConstructorArguments[0].Value as TypeDefinition;
}

isFactoryOrStatic = IsFactoryOrStatic(Generator, this, exclusiveToType);
InterfaceKind = GetInterfaceKind(Generator, this, exclusiveToType);

rawMethodDeclarations = methods.Select(m => m.GetRawDeclaration()).ToList();
wrapperMethodDeclarations = methods.Select(m => m.GetWrapperDefinition()).ToList();
Expand All @@ -76,7 +84,7 @@ public override void Emit()
genericWithBounds = "<" + String.Join(", ", t.GenericParameters.Select(p => p.Name + ": RtType")) + ">";
}

string prependStatic = isFactoryOrStatic ? "static " : "";
string prependStatic = (InterfaceKind == InterfaceKind.Factory || InterfaceKind == InterfaceKind.Statics) ? "static " : "";

var rawMethodDeclarationsWithFeatures = new List<string>();
var lastFeatureAttr = definitionFeatureConditions.LastOrDefault()?.GetAttribute();
Expand Down Expand Up @@ -122,7 +130,7 @@ public override void Emit()
}
}

private bool IsFactoryOrStatic(Generator gen, TypeDef t, TypeDefinition exclusiveToType)
private InterfaceKind GetInterfaceKind(Generator gen, TypeDef t, TypeDefinition exclusiveToType)
{
var trimmedName = t.Name.TrimEnd('1', '2', '3', '4', '5', '6', '7', '8', '9');
var guessedFromName = trimmedName.EndsWith("Factory") || trimmedName.EndsWith("Statics");
Expand All @@ -149,7 +157,12 @@ private bool IsFactoryOrStatic(Generator gen, TypeDef t, TypeDefinition exclusiv
}
}

return candidates.Any(c => c.GetFactoryTypes().Contains(t.Type) || c.GetStaticTypes().Contains(t.Type));
if (candidates.Any(c => c.GetFactoryTypes().Contains(t.Type)))
return InterfaceKind.Factory;
else if (candidates.Any(c => c.GetStaticTypes().Contains(t.Type)))
return InterfaceKind.Statics;
else
return InterfaceKind.Instance;
}
}
}
199 changes: 108 additions & 91 deletions Generator/Types/MethodDef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,121 @@ public class MethodDetailsCache
public string RawName;
public string[] InputParameterNames;
public Tuple<TypeReference, InputKind>[] InputParameterTypes;
public TypeReference[] OutTypes;
public string WrapperBody;
public Tuple<TypeReference, bool>[] OutTypes;
public string GetManyParameterName;
public List<Tuple<string, TypeReference, bool>> Output;

public string[] MakeInputParameters(Generator generator, ITypeRequestSource source)
{
return InputParameterNames.Zip(InputParameterTypes, (name, t) => name + ": " + TypeHelpers.GetInputTypeName(generator, source, t.Item1, t.Item2)).ToArray();
}

public string MakeOutType(Generator generator, ITypeRequestSource source)
public string MakeOutType(Generator generator, ITypeRequestSource source, bool isFactoryMethod)
{
string outType = String.Join(", ", OutTypes.Select(o => TypeHelpers.GetTypeName(generator, source, o, TypeUsage.Out)));
string outType = String.Join(", ", OutTypes.Select(o => TypeHelpers.GetTypeName(generator, source, o.Item1, (o.Item2 || isFactoryMethod) ? TypeUsage.OutNonNull : TypeUsage.Out)));
if (OutTypes.Count() != 1)
{
outType = "(" + outType + ")"; // also works for count == 0 (empty tuple)
}
return outType;
}

public string MakeWrapperBody(MethodDefinition def, bool isFactoryMethod)
{
var rawName = RawName;
bool isGetMany = GetManyParameterName != null;
var getManyPname = GetManyParameterName;
var output = Output;

var rawParams = new List<string> { "self as *const _ as *mut _" };
foreach (var p in def.Parameters)
{
var pname = NameHelpers.PreventKeywords(NameHelpers.FirstToLower(p.Name));
if (p.ParameterType.IsByReference)
{
if (((ByReferenceType)p.ParameterType).ElementType.IsArray)
{
rawParams.Add("&mut " + pname + "Size");
}

// output parameter
rawParams.Add("&mut " + pname);
}
else
{
// input parameter
if (p.ParameterType.IsArray)
{
if (p.IsOut)
{
if (isGetMany)
{
rawParams.Add(pname + ".capacity() as u32");
rawParams.Add(pname + ".as_mut_ptr() as *mut T::Abi");
}
else
{
rawParams.Add(pname + ".len() as u32");
rawParams.Add(pname + ".as_mut_ptr() as *mut _");
}
}
else
{
rawParams.Add(pname + ".len() as u32");
rawParams.Add(pname + ".as_ptr() as *mut _");
}
}
else
{
rawParams.Add(TypeHelpers.UnwrapInputParameter(pname, p.ParameterType));
}
}
}

if (def.ReturnType.FullName != "System.Void")
{
if (def.ReturnType.IsArray)
{
rawParams.Add("&mut outSize");
}
rawParams.Add("&mut out");
}

var outInit = String.Join(" ", output.SelectMany(o => TypeHelpers.CreateUninitializedOutputs(o.Item1, o.Item2)));
if (outInit != "") outInit = "\r\n " + outInit;

var outWrap = String.Join(", ", output.Select(o => TypeHelpers.WrapOutputParameter(o.Item1, o.Item2, isFactoryMethod || o.Item3)));
if (output.Count != 1)
{
outWrap = "(" + outWrap + ")"; // also works for count == 0 (empty tuple)
}
outWrap = "Ok(" + outWrap + ")";

if (isGetMany)
{
outInit = $"\r\n debug_assert!({ getManyPname }.capacity() > 0, \"capacity of `{ getManyPname }` must not be 0 (use Vec::with_capacity)\"); { getManyPname }.clear();{ outInit }";
outWrap = $"{ getManyPname }.set_len(out as usize); Ok(())";
}

return outInit + $@"
let hr = ((*self.lpVtbl).{ rawName })({ String.Join(", ", rawParams) });
if hr == S_OK {{ { outWrap } }} else {{ err(hr) }}";
}
}

public class MethodDef : ITypeRequestSource
{
public TypeDef DeclaringType { get; private set; }
public MethodDefinition Method { get; private set; }
public bool IsFactoryMethod
{
get
{
var kind = ((InterfaceDef)DeclaringType).InterfaceKind;
if (kind == InterfaceKind.Unidentified) { throw new InvalidOperationException(); }
return kind == InterfaceKind.Factory;
}
}

public Module Module
{
Expand Down Expand Up @@ -120,10 +212,10 @@ public string GetWrapperName(string rawName)
public string GetWrapperDefinition()
{
var inputParameters = Details.MakeInputParameters(DeclaringType.Generator, this);
var outType = Details.MakeOutType(DeclaringType.Generator, this);
var outType = Details.MakeOutType(DeclaringType.Generator, this, IsFactoryMethod);

return $@"#[inline] pub unsafe fn { Details.WrappedName }({ String.Join(", ", new string[] { "&self" }.Concat(inputParameters)) }) -> Result<{ outType }> {{{ Details.WrapperBody }
}}";
return $@"#[inline] pub fn { Details.WrappedName }({ String.Join(", ", new string[] { "&self" }.Concat(inputParameters)) }) -> Result<{ outType }> {{ unsafe {{ { Details.MakeWrapperBody(Method, IsFactoryMethod) }
}}}}";
}

private MethodDetailsCache InitializeDetailsCache()
Expand All @@ -139,7 +231,7 @@ private MethodDetailsCache InitializeDetailsCache()
// It uses the __RPC__out_ecount_part(capacity, *actual) annotation in the C headers. For the wrapper we use a &mut Vec<> buffer.

var input = new List<Tuple<string, TypeReference, InputKind>>();
var output = new List<Tuple<string, TypeReference>>();
var output = new List<Tuple<string, TypeReference, bool>>();

foreach (var p in Method.Parameters)
{
Expand All @@ -148,7 +240,7 @@ private MethodDetailsCache InitializeDetailsCache()
{
Assert(p.IsOut);
var realType = ((ByReferenceType)p.ParameterType).ElementType;
output.Add(Tuple.Create(pname, realType));
output.Add(Tuple.Create(pname, realType, false));
}
else
{
Expand Down Expand Up @@ -182,17 +274,18 @@ private MethodDetailsCache InitializeDetailsCache()
if (Method.ReturnType.FullName != "System.Void")
{
// this makes the actual return value the last in the tuple (if multiple)
output.Add(Tuple.Create("out", Method.ReturnType));
output.Add(Tuple.Create("out", Method.ReturnType, TypeHelpers.IsReturnTypeNonNull(Method.ReturnType, DeclaringType.Generator)));
}

var outTypes = output.Select(o => o.Item2).ToArray();
// TODO: second tuple element should be true for some method's return value
var outTypes = output.Select(o => Tuple.Create(o.Item2, o.Item3)).ToArray();

if (isGetMany)
{
outTypes = new TypeReference[] { }; // GetMany has no return value
outTypes = new Tuple<TypeReference, bool>[] { }; // GetMany has no return value
}



return new MethodDetailsCache
{
Expand All @@ -201,87 +294,11 @@ private MethodDetailsCache InitializeDetailsCache()
InputParameterNames = input.Select(i => i.Item1).ToArray(),
InputParameterTypes = input.Select(i => Tuple.Create(i.Item2, i.Item3)).ToArray(),
OutTypes = outTypes.ToArray(),
WrapperBody = GetWrapperBody(rawName, isGetMany, getManyPname, output)
GetManyParameterName = isGetMany ? getManyPname : null,
Output = output
};
}

private string GetWrapperBody(string rawName, bool isGetMany, string getManyPname, List<Tuple<string, TypeReference>> output)
{
var rawParams = new List<string> { "self as *const _ as *mut _" };
foreach (var p in Method.Parameters)
{
var pname = NameHelpers.PreventKeywords(NameHelpers.FirstToLower(p.Name));
if (p.ParameterType.IsByReference)
{
if (((ByReferenceType)p.ParameterType).ElementType.IsArray)
{
rawParams.Add("&mut " + pname + "Size");
}

// output parameter
rawParams.Add("&mut " + pname);
}
else
{
// input parameter
if (p.ParameterType.IsArray)
{
if (p.IsOut)
{
if (isGetMany)
{
rawParams.Add(pname + ".capacity() as u32");
rawParams.Add(pname + ".as_mut_ptr() as *mut T::Abi");
}
else
{
rawParams.Add(pname + ".len() as u32");
rawParams.Add(pname + ".as_mut_ptr() as *mut _");
}
}
else
{
rawParams.Add(pname + ".len() as u32");
rawParams.Add(pname + ".as_ptr() as *mut _");
}
}
else
{
rawParams.Add(TypeHelpers.UnwrapInputParameter(pname, p.ParameterType));
}
}
}

if (Method.ReturnType.FullName != "System.Void")
{
if (Method.ReturnType.IsArray)
{
rawParams.Add("&mut outSize");
}
rawParams.Add("&mut out");
}

var outInit = String.Join(" ", output.SelectMany(o => TypeHelpers.CreateUninitializedOutputs(o.Item1, o.Item2)));
if (outInit != "") outInit = "\r\n " + outInit;

var outWrap = String.Join(", ", output.Select(o => TypeHelpers.WrapOutputParameter(o.Item1, o.Item2)));
if (output.Count != 1)
{
outWrap = "(" + outWrap + ")"; // also works for count == 0 (empty tuple)
}
outWrap = "Ok(" + outWrap + ")";

if (isGetMany)
{
outInit = $"\r\n debug_assert!({ getManyPname }.capacity() > 0, \"capacity of `{ getManyPname }` must not be 0 (use Vec::with_capacity)\"); { getManyPname }.clear();{ outInit }";
outWrap = $"{ getManyPname }.set_len(out as usize); Ok(())";
}

return outInit + $@"
let hr = ((*self.lpVtbl).{ rawName })({ String.Join(", ", rawParams) });
if hr == S_OK {{ { outWrap } }} else {{ err(hr) }}";
}

public string GetRawDeclaration()
{
var name = GetRawName();
Expand Down
12 changes: 11 additions & 1 deletion Generator/Types/TypeDef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,17 @@ public string GetPath(Module requestingModule)
}
else
{
name = $"::rt::gen::{ Module.Path }::{ Name }";
if (Module.Path.StartsWith("windows::foundation"))
{
var shortened = Module.Path.Substring("windows::foundation".Length);
if (shortened.Length > 0)
shortened = shortened.Substring(2) + "::";
name = $"foundation::{ shortened }{ Name }";
}
else
{
name = $"::rt::gen::{ Module.Path }::{ Name }";
}
var relative = $"{ Module.GetRelativePath(requestingModule) }::{ Name }";
if (relative.Length < name.Length)
name = relative;
Expand Down
Loading