Skip to content

Commit 93e7050

Browse files
authored
Merge pull request #784 from Excel-DNA/NativeAOT
Improved extended registration in NativeAOT
2 parents 3dbc995 + 37c47bd commit 93e7050

File tree

9 files changed

+357
-9
lines changed

9 files changed

+357
-9
lines changed

Source/ExcelDna.Integration/Registration/ParamsRegistration.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ static LambdaExpression WrapMethodParams(LambdaExpression functionLambda)
144144
*
145145
*/
146146

147-
int maxArguments = 125; // Constrained by 255 char registration string, take off 3 type chars, use up to 2 chars per param (before we start doing object...) (& also return)
148-
// CONSIDER: Might improve this if we generate the delegate based on the max length...
147+
int maxArguments = NativeAOT.IsActive ? 16 : 125; // Constrained by 255 char registration string, take off 3 type chars, use up to 2 chars per param (before we start doing object...) (& also return)
148+
// CONSIDER: Might improve this if we generate the delegate based on the max length...
149149

150150
var normalParams = functionLambda.Parameters.Take(functionLambda.Parameters.Count() - 1).ToList();
151151
var normalParamCount = normalParams.Count;
@@ -210,13 +210,19 @@ static LambdaExpression WrapMethodParams(LambdaExpression functionLambda)
210210
if (maxArguments == 125)
211211
{
212212
delegateType = typeof(CustomFunc125<,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,>)
213-
.MakeGenericType(allParamTypes.ToArray());
213+
.MakeGenericType(allParamTypes.ToArray());
214214
}
215-
else // if (maxArguments == 29)
215+
else if (maxArguments == 29)
216216
{
217217
delegateType = typeof(CustomFunc29<,,,,,,,,,,,,,,,,,,,,,,,,,,,,,>)
218218
.MakeGenericType(allParamTypes.ToArray());
219219
}
220+
else // if (maxArguments == 16)
221+
{
222+
delegateType = typeof(Func<,,,,,,,,,,,,,,,,>)
223+
.MakeGenericType(allParamTypes.ToArray());
224+
}
225+
220226
return Expression.Lambda(delegateType, blockExpr, allParamExprs);
221227
}
222228
}

Source/ExcelDna.SourceGenerator.NativeAOT/Generator.cs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ public void Execute(GeneratorExecutionContext context)
3030

3131
string source = """
3232
// <auto-generated/>
33+
using System;
34+
using System.Collections.Generic;
35+
using System.Reflection;
3336
using System.Runtime.CompilerServices;
3437
using System.Runtime.InteropServices;
3538
@@ -44,7 +47,7 @@ public static short Initialize(void* xlAddInExportInfoAddress, void* hModuleXll,
4447
4548
[ADDINS]
4649
47-
[FUNCTIONS]
50+
[FUNCTIONS]
4851
4952
return ExcelDna.ManagedHost.AddInInitialize.InitializeNativeAOT(xlAddInExportInfoAddress, hModuleXll, pPathXLL, disableAssemblyContextUnload, pTempDirPath);
5053
}
@@ -68,16 +71,26 @@ public static short Initialize(void* xlAddInExportInfoAddress, void* hModuleXll,
6871
source = source.Replace("[ADDINS]", addIns);
6972
}
7073
{
71-
string functions = "List<Type> functionTypes = new List<Type>();\r\n";
74+
string functions = "List<Type> typeRefs = new List<Type>();\r\n";
75+
string methods = "List<MethodInfo> methodRefs = new List<MethodInfo>();\r\n";
7276
foreach (var i in receiver.Functions)
7377
{
7478
functions += $"ExcelDna.Integration.NativeAOT.MethodsForRegistration.Add(typeof({Util.GetFullTypeName(i.ContainingType)}).GetMethod(\"{i.Name}\")!);\r\n";
75-
functions += $"functionTypes.Add(typeof({Util.MethodType(i)}));\r\n";
79+
functions += $"typeRefs.Add(typeof({Util.MethodType(i)}));\r\n";
7680
foreach (var p in i.Parameters)
77-
functions += $"functionTypes.Add(typeof(Func<object, {Util.GetFullTypeName(p.Type)}>));\r\n";
81+
{
82+
functions += $"typeRefs.Add(typeof(Func<object, {Util.GetFullTypeName(p.Type)}>));\r\n";
83+
}
84+
85+
if (i.Parameters.Length > 0 && i.Parameters.Last().IsParams && i.Parameters.Last().Type is IArrayTypeSymbol arrayType)
86+
{
87+
methods += $"methodRefs.Add(typeof(List<{Util.GetFullTypeName(arrayType.ElementType)}>).GetMethod(\"ToArray\")!);\r\n";
88+
functions += $"typeRefs.Add(typeof(Func<{Util.CreateFunc16Args(i)}>));\r\n";
89+
}
90+
7891
functions += "\r\n";
7992
}
80-
source = source.Replace("[FUNCTIONS]", functions);
93+
source = source.Replace("[FUNCTIONS]", functions + methods);
8194
}
8295

8396
context.AddSource($"ExcelDna.SG.NAOT.Init.g.cs", source);

Source/ExcelDna.SourceGenerator.NativeAOT/Util.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ public static string MethodType(IMethodSymbol method)
3939
$"Func<{(string.IsNullOrWhiteSpace(parameters) ? null : $"{parameters}, ")}{GetFullTypeName(method.ReturnType)}>";
4040
}
4141

42+
public static string CreateFunc16Args(IMethodSymbol method)
43+
{
44+
List<ITypeSymbol?> allParamTypes = method.Parameters.Take(method.Parameters.Length - 1).Select(p => p.Type).Cast<ITypeSymbol?>().ToList();
45+
var toAdd = 16 - allParamTypes.Count;
46+
for (int i = 0; i < toAdd; i++)
47+
{
48+
allParamTypes.Add(null);
49+
}
50+
allParamTypes.Add(method.ReturnType);
51+
52+
return string.Join(",", allParamTypes.Select(i => i == null ? "object" : GetFullTypeName(i)));
53+
}
54+
4255
private static SymbolDisplayFormat FullNameFormat = new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);
4356
}
4457
}

Source/ExcelDna.sln

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExcelDna.AddIn.RuntimeTests
132132
{D50C3A8E-46F1-F61B-C8F5-ECDA05995EEB} = {D50C3A8E-46F1-F61B-C8F5-ECDA05995EEB}
133133
EndProjectSection
134134
EndProject
135+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExcelDna.SourceGenerator.NativeAOT.Tests", "Tests\ExcelDna.SourceGenerator.NativeAOT.Tests\ExcelDna.SourceGenerator.NativeAOT.Tests.csproj", "{D47CC5BF-712E-4FFA-BE52-E783BDCC6D33}"
136+
EndProject
135137
Global
136138
GlobalSection(SolutionConfigurationPlatforms) = preSolution
137139
Debug|Win32 = Debug|Win32
@@ -292,6 +294,14 @@ Global
292294
{1EC2EE86-7C59-4CA9-9D6A-FBF206DA9D3F}.Release|Win32.ActiveCfg = Release|Any CPU
293295
{1EC2EE86-7C59-4CA9-9D6A-FBF206DA9D3F}.Release|x64.ActiveCfg = Release|Any CPU
294296
{1EC2EE86-7C59-4CA9-9D6A-FBF206DA9D3F}.Release|x64.Build.0 = Release|Any CPU
297+
{D47CC5BF-712E-4FFA-BE52-E783BDCC6D33}.Debug|Win32.ActiveCfg = Debug|Any CPU
298+
{D47CC5BF-712E-4FFA-BE52-E783BDCC6D33}.Debug|Win32.Build.0 = Debug|Any CPU
299+
{D47CC5BF-712E-4FFA-BE52-E783BDCC6D33}.Debug|x64.ActiveCfg = Debug|Any CPU
300+
{D47CC5BF-712E-4FFA-BE52-E783BDCC6D33}.Debug|x64.Build.0 = Debug|Any CPU
301+
{D47CC5BF-712E-4FFA-BE52-E783BDCC6D33}.Release|Win32.ActiveCfg = Release|Any CPU
302+
{D47CC5BF-712E-4FFA-BE52-E783BDCC6D33}.Release|Win32.Build.0 = Release|Any CPU
303+
{D47CC5BF-712E-4FFA-BE52-E783BDCC6D33}.Release|x64.ActiveCfg = Release|Any CPU
304+
{D47CC5BF-712E-4FFA-BE52-E783BDCC6D33}.Release|x64.Build.0 = Release|Any CPU
295305
EndGlobalSection
296306
GlobalSection(SolutionProperties) = preSolution
297307
HideSolutionNode = FALSE
@@ -311,6 +321,7 @@ Global
311321
{7CA501E1-FB74-4903-B34D-EFE513C5B428} = {511CE610-5E55-457F-B818-1522D1A96727}
312322
{151AE960-6320-402D-BDD0-D76DF96F2BD7} = {511CE610-5E55-457F-B818-1522D1A96727}
313323
{1EC2EE86-7C59-4CA9-9D6A-FBF206DA9D3F} = {511CE610-5E55-457F-B818-1522D1A96727}
324+
{D47CC5BF-712E-4FFA-BE52-E783BDCC6D33} = {511CE610-5E55-457F-B818-1522D1A96727}
314325
EndGlobalSection
315326
GlobalSection(ExtensibilityGlobals) = postSolution
316327
SolutionGuid = {69EF43B0-B903-4775-BC81-C7E7E012EDA3}

Source/Tests/ExcelDna.AddIn.RuntimeTestsAOT/Functions.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,5 +106,73 @@ public static string NativeRangeAddress(IRange r)
106106
{
107107
return "Native Address: " + r.Get<string>("Address");
108108
}
109+
110+
[ExcelFunction]
111+
public static string NativeEnum(DateTimeKind e)
112+
{
113+
return "Native Enum VAL: " + e.ToString();
114+
}
115+
116+
[ExcelFunction]
117+
public static DateTimeKind NativeEnumReturn(string s)
118+
{
119+
return Enum.Parse<DateTimeKind>(s);
120+
}
121+
122+
[ExcelFunction]
123+
public static string NativeStringArray(string[] s)
124+
{
125+
return "Native StringArray VALS: " + string.Concat(s);
126+
}
127+
128+
[ExcelFunction]
129+
public static string NativeStringArray2D(string[,] s)
130+
{
131+
string result = "";
132+
for (int i = 0; i < s.GetLength(0); i++)
133+
{
134+
for (int j = 0; j < s.GetLength(1); j++)
135+
{
136+
result += s[i, j];
137+
}
138+
139+
result += " ";
140+
}
141+
142+
return $"Native StringArray2D VALS: {result}";
143+
}
144+
145+
[ExcelFunction]
146+
public static string NativeParamsFunc1(
147+
[ExcelArgument(Name = "first.Input", Description = "is a useful start")]
148+
object input,
149+
[ExcelArgument(Description = "is another param start")]
150+
string QtherInpEt,
151+
[ExcelArgument(Name = "Value", Description = "gives the Rest")]
152+
params object[] args)
153+
{
154+
return input + "," + QtherInpEt + ", : " + args.Length;
155+
}
156+
157+
[ExcelFunction]
158+
public static string NativeParamsFunc2(
159+
[ExcelArgument(Name = "first.Input", Description = "is a useful start")]
160+
object input,
161+
[ExcelArgument(Name = "second.Input", Description = "is some more stuff")]
162+
string input2,
163+
[ExcelArgument(Description = "is another param ")]
164+
string QtherInpEt,
165+
[ExcelArgument(Name = "Value", Description = "gives the Rest")]
166+
params object[] args)
167+
{
168+
var content = string.Join(",", args.Select(ValueType => ValueType.ToString()));
169+
return input + "," + input2 + "," + QtherInpEt + ", " + $"[{args.Length}: {content}]";
170+
}
171+
172+
[ExcelFunction]
173+
public static string NativeParamsJoinString(string separator, params string[] values)
174+
{
175+
return String.Join(separator, values);
176+
}
109177
}
110178
}

Source/Tests/ExcelDna.RuntimeTests/NativeAOT.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,5 +139,95 @@ public void Range()
139139
functionRange3.Formula = "=NativeRangeAddress((B2,D5:E6))";
140140
Assert.Equal("Native Address: $B$2,$D$5:$E$6", functionRange3.Value.ToString());
141141
}
142+
143+
[ExcelFact(Workbook = "", AddIn = AddInPath.RuntimeTestsAOT)]
144+
public void Enum()
145+
{
146+
Range functionRange = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["B1:B1"];
147+
functionRange.Formula = "=NativeEnum(\"Unspecified\")";
148+
Assert.Equal("Native Enum VAL: Unspecified", functionRange.Value.ToString());
149+
150+
functionRange.Formula = "=NativeEnum(\"Local\")";
151+
Assert.Equal("Native Enum VAL: Local", functionRange.Value.ToString());
152+
153+
functionRange.Formula = "=NativeEnum(1)";
154+
Assert.Equal("Native Enum VAL: Utc", functionRange.Value.ToString());
155+
}
156+
157+
[ExcelFact(Workbook = "", AddIn = AddInPath.RuntimeTestsAOT)]
158+
public void EnumReturn()
159+
{
160+
Range functionRange = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["B1:B1"];
161+
functionRange.Formula = "=NativeEnumReturn(\"Unspecified\")";
162+
Assert.Equal("Unspecified", functionRange.Value.ToString());
163+
164+
functionRange.Formula = "=NativeEnumReturn(\"Local\")";
165+
Assert.Equal("Local", functionRange.Value.ToString());
166+
}
167+
168+
[ExcelFact(Workbook = "", AddIn = AddInPath.RuntimeTestsAOT)]
169+
public void StringArray()
170+
{
171+
Range a1 = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["A1:A1"];
172+
a1.Value = "01";
173+
174+
Range a2 = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["A2:A2"];
175+
a2.Value = "2.30";
176+
177+
Range a3 = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["A3:A3"];
178+
a3.Value = "World";
179+
180+
Range functionRange = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["B1:B1"];
181+
functionRange.Formula = "=NativeStringArray(A1:A3)";
182+
183+
Assert.Equal("Native StringArray VALS: 12.3World", functionRange.Value.ToString());
184+
}
185+
186+
[ExcelFact(Workbook = "", AddIn = AddInPath.RuntimeTestsAOT)]
187+
public void StringArray2D()
188+
{
189+
Range a1 = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["A1"];
190+
a1.Value = "01";
191+
192+
Range a2 = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["A2"];
193+
a2.Value = "2.30";
194+
195+
Range a3 = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["A3"];
196+
a3.Value = "Hello";
197+
198+
Range b1 = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["B1"];
199+
b1.Value = "5";
200+
201+
Range b2 = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["B2"];
202+
b2.Value = "6.7";
203+
204+
Range b3 = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["B3"];
205+
b3.Value = "World";
206+
207+
Range functionRange = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["C1"];
208+
functionRange.Formula = "=NativeStringArray2D(A1:B3)";
209+
210+
Assert.Equal("Native StringArray2D VALS: 15 2.36.7 HelloWorld ", functionRange.Value.ToString());
211+
}
212+
213+
[ExcelFact(Workbook = "", AddIn = AddInPath.RuntimeTestsAOT)]
214+
public void Params()
215+
{
216+
{
217+
Range functionRange = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["B1"];
218+
functionRange.Formula = "=NativeParamsFunc1(1,\"2\",4,5)";
219+
Assert.Equal("1,2, : 2", functionRange.Value.ToString());
220+
}
221+
{
222+
Range functionRange = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["B2"];
223+
functionRange.Formula = "=NativeParamsFunc2(\"a\",,\"c\",\"d\",,\"f\")";
224+
Assert.Equal("a,,c, [3: d,ExcelDna.Integration.ExcelMissing,f]", functionRange.Value.ToString());
225+
}
226+
{
227+
Range functionRange = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["B3"];
228+
functionRange.Formula = "=NativeParamsJoinString(\"//\",\"5\",\"4\",\"3\")";
229+
Assert.Equal("5//4//3", functionRange.Value.ToString());
230+
}
231+
}
142232
}
143233
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0-windows</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
8+
<IsPackable>false</IsPackable>
9+
<IsTestProject>true</IsTestProject>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.4.0" />
14+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.2" />
15+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
16+
<PackageReference Include="xunit" Version="2.5.3" />
17+
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
18+
</ItemGroup>
19+
20+
<ItemGroup>
21+
<ProjectReference Include="..\..\ExcelDna.Integration\ExcelDna.Integration.csproj" />
22+
<ProjectReference Include="..\..\ExcelDna.Loader\ExcelDna.Loader.csproj" />
23+
<ProjectReference Include="..\..\ExcelDna.ManagedHost\ExcelDna.ManagedHost.csproj" />
24+
<ProjectReference Include="..\..\ExcelDna.SourceGenerator.NativeAOT\ExcelDna.SourceGenerator.NativeAOT.csproj" />
25+
</ItemGroup>
26+
27+
<ItemGroup>
28+
<Using Include="Xunit" />
29+
</ItemGroup>
30+
31+
</Project>

0 commit comments

Comments
 (0)