Skip to content

Commit

Permalink
Stub generation fixes (#154)
Browse files Browse the repository at this point in the history
  • Loading branch information
KasperJSdeVries authored May 10, 2022
1 parent 3f87195 commit 4da4bac
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ struct {{ClassName}}
// Helper Functions to access fields of managed object
// Declaration of stubs. These functions are implemented by Interop code developers
{{#each Functions}}
{{#if IsStatic}}static {{/if}}{{ReturnType}} {{DeclarationForUserCode}};
static {{ReturnType}} {{DeclarationForUserCode}};
{{/each}}
};
}
Expand Down
28 changes: 19 additions & 9 deletions MetadataProcessor.Shared/nanoSkeletonGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,14 @@ private void GenerateStubs()
{
// get the parameter type
string parameterType = string.Empty;
string parameterTypeWORef = string.Empty;
string parameterTypeClr = string.Empty;

if (item.ParameterType.IsByReference)
{
// for ref types need an extra step to get the element type
parameterType = item.ParameterType.GetElementType().ToNativeTypeAsString() + "&";
parameterTypeWORef = item.ParameterType.GetElementType().ToNativeTypeAsString();
parameterTypeClr = item.ParameterType.GetElementType().ToCLRTypeAsString();
}
else
Expand All @@ -169,16 +171,24 @@ private void GenerateStubs()
}

// compose the function declaration
declaration.Append($"{parameterType} param{parameterIndex.ToString()}, ");
declaration.Append($"{parameterType} param{parameterIndex}, ");

// compose the function call
marshallingCall.Append($"param{parameterIndex.ToString()}, ");
if (item.ParameterType.IsByReference)
{
marshallingCall.Append($"*param{parameterIndex}, ");
}
else
{
marshallingCall.Append($"param{parameterIndex}, ");
}


// compose the variable block
var parameterDeclaration = new ParameterDeclaration()
{
Index = parameterIndex.ToString(),
Name = $"param{parameterIndex.ToString()}",
Name = $"param{parameterIndex}",
};

if(item.ParameterType.IsByReference)
Expand All @@ -190,10 +200,10 @@ private void GenerateStubs()
parameterDeclaration.Type = parameterType;

parameterDeclaration.Declaration =
$"{parameterType} {parameterDeclaration.Name};" + Environment.NewLine +
$" UINT8 heapblock{parameterIndex.ToString()}[CLR_RT_HEAP_BLOCK_SIZE];";
$"{parameterTypeWORef} *{parameterDeclaration.Name};" + Environment.NewLine +
$" uint8_t heapblock{parameterIndex}[CLR_RT_HEAP_BLOCK_SIZE];";

parameterDeclaration.MarshallingDeclaration = $"Interop_Marshal_{parameterTypeClr}_ByRef( stack, heapblock{(parameterIndex + (m.IsStatic ? 0 : 1)).ToString()}, {parameterDeclaration.Name} )";
parameterDeclaration.MarshallingDeclaration = $"Interop_Marshal_{parameterTypeClr}_ByRef( stack, heapblock{parameterIndex}, {(parameterIndex + (m.IsStatic ? 0 : 1))}, {parameterDeclaration.Name} )";

}
else if (item.ParameterType.IsArray)
Expand All @@ -203,7 +213,7 @@ private void GenerateStubs()

parameterDeclaration.Type = parameterType;
parameterDeclaration.Declaration = $"{parameterType} {parameterDeclaration.Name};";
parameterDeclaration.MarshallingDeclaration = $"Interop_Marshal_{parameterTypeClr}( stack, {(parameterIndex + (m.IsStatic ? 0 : 1)).ToString()}, {parameterDeclaration.Name} )";
parameterDeclaration.MarshallingDeclaration = $"Interop_Marshal_{parameterTypeClr}( stack, {(parameterIndex + (m.IsStatic ? 0 : 1))}, {parameterDeclaration.Name} )";
}
else
{
Expand All @@ -212,8 +222,8 @@ private void GenerateStubs()

parameterDeclaration.Type = parameterType;
parameterDeclaration.Declaration = $"{parameterType} {parameterDeclaration.Name};";
parameterDeclaration.MarshallingDeclaration = $"Interop_Marshal_{parameterTypeClr}( stack, {(parameterIndex + (m.IsStatic ? 0 : 1)).ToString()}, {parameterDeclaration.Name} )";
}
parameterDeclaration.MarshallingDeclaration = $"Interop_Marshal_{parameterTypeClr}( stack, {(parameterIndex + (m.IsStatic ? 0 : 1))}, {parameterDeclaration.Name} )";
}
newMethod.ParameterDeclaration.Add(parameterDeclaration);
parameterIndex++;
}
Expand Down
189 changes: 176 additions & 13 deletions MetadataProcessor.Tests/Core/StubsGenerationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ namespace nanoFramework.Tools.MetadataProcessor.Tests.Core
[TestClass]
public class StubsGenerationTests
{
private const string NativeMethodGenerationDeclaration = @"void NativeMethodGeneration::NativeMethodWithReferenceParameters( uint8_t& param0, uint16_t& param1, HRESULT &hr )
private const string NativeMethodGenerationDeclaration =
@"void NativeMethodGeneration::NativeMethodWithReferenceParameters( uint8_t& param0, uint16_t& param1, HRESULT &hr )
{
(void)param0;
Expand All @@ -33,8 +34,171 @@ public class StubsGenerationTests
}";

private const string NativeMarshallingMethodGenerationDeclaration =
@"HRESULT Library_StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration::NativeMethodWithReferenceParameters___VOID__BYREF_U1__BYREF_U2( CLR_RT_StackFrame& stack )
{
NANOCLR_HEADER(); hr = S_OK;
{
uint8_t *param0;
uint8_t heapblock0[CLR_RT_HEAP_BLOCK_SIZE];
NANOCLR_CHECK_HRESULT( Interop_Marshal_UINT8_ByRef( stack, heapblock0, 1, param0 ) );
uint16_t *param1;
uint8_t heapblock1[CLR_RT_HEAP_BLOCK_SIZE];
NANOCLR_CHECK_HRESULT( Interop_Marshal_UINT16_ByRef( stack, heapblock1, 2, param1 ) );
NativeMethodGeneration::NativeMethodWithReferenceParameters( *param0, *param1, hr );
NANOCLR_CHECK_HRESULT( hr );
}
NANOCLR_NOCLEANUP();
}";

private const string NativeHeaderMethodGenerationDeclaration =
"static void NativeMethodWithReferenceParameters( uint8_t& param0, uint16_t& param1, HRESULT &hr );";

private string stubPath;

[TestMethod]
public void GeneratingStubsFromNFAppTest()
{
// read generated stub file and look for the function declaration
var generatedFile =
File.ReadAllText(
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.cpp");

Assert.IsTrue(generatedFile.Contains(NativeMethodGenerationDeclaration));
}

[TestMethod]
public void GeneratingMarshallingStubsFromNFAppTest()
{
var generatedFile =
File.ReadAllText(
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration_mshl.cpp");

Assert.IsTrue(generatedFile.Contains(NativeMarshallingMethodGenerationDeclaration));
}

[TestMethod]
public void GeneratingHeaderStubsFromNFAppTest()
{
var generatedFile =
File.ReadAllText(
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.h");

Assert.IsTrue(generatedFile.Contains(NativeHeaderMethodGenerationDeclaration));
}

private const string StaticMethodWithoutParameterHeaderGeneration =
@"static void NativeStaticMethod( HRESULT &hr );";
private const string StaticMethodWithoutParameterMarshallGeneration =
@"HRESULT Library_StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration::NativeStaticMethod___STATIC__VOID( CLR_RT_StackFrame& stack )
{
NANOCLR_HEADER(); hr = S_OK;
{
NativeMethodGeneration::NativeStaticMethod( hr );
NANOCLR_CHECK_HRESULT( hr );
}
NANOCLR_NOCLEANUP();
}";
private const string StaticMethodWithoutParameterImplementationGeneration =
@"void NativeMethodGeneration::NativeStaticMethod( HRESULT &hr )
{
(void)hr;
////////////////////////////////
// implementation starts here //
// implementation ends here //
////////////////////////////////
}";

[TestMethod]
public void GeneratingStaticMethodWithoutParams()
{
var generatedHeaderFile =
File.ReadAllText(
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.h");

var generatedMarshallFile =
File.ReadAllText(
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration_mshl.cpp");

var generatedImplementationFile =
File.ReadAllText(
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.cpp");

Assert.IsTrue(generatedHeaderFile.Contains(StaticMethodWithoutParameterHeaderGeneration));
Assert.IsTrue(generatedMarshallFile.Contains(StaticMethodWithoutParameterMarshallGeneration));
Assert.IsTrue(generatedImplementationFile.Contains(StaticMethodWithoutParameterImplementationGeneration));
}

private const string StaticMethodHeaderGeneration =
@"static uint8_t NativeStaticMethodReturningByte( char param0, HRESULT &hr );";
private const string StaticMethodMarshallGeneration =
@"HRESULT Library_StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration::NativeStaticMethodReturningByte___STATIC__U1__CHAR( CLR_RT_StackFrame& stack )
{
NANOCLR_HEADER(); hr = S_OK;
{
char param0;
NANOCLR_CHECK_HRESULT( Interop_Marshal_CHAR( stack, 0, param0 ) );
uint8_t retValue = NativeMethodGeneration::NativeStaticMethodReturningByte( param0, hr );
NANOCLR_CHECK_HRESULT( hr );
SetResult_UINT8( stack, retValue );
}
NANOCLR_NOCLEANUP();
}";
private const string StaticMethodImplementationGeneration =
@"uint8_t NativeMethodGeneration::NativeStaticMethodReturningByte( char param0, HRESULT &hr )
{
(void)param0;
(void)hr;
uint8_t retValue = 0;
////////////////////////////////
// implementation starts here //
// implementation ends here //
////////////////////////////////
return retValue;
}";

[TestMethod]
public void GeneratingStaticMethod()
{
var generatedHeaderFile =
File.ReadAllText(
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.h");

var generatedMarshallFile =
File.ReadAllText(
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration_mshl.cpp");

var generatedImplementationFile =
File.ReadAllText(
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.cpp");

Assert.IsTrue(generatedHeaderFile.Contains(StaticMethodHeaderGeneration));
Assert.IsTrue(generatedMarshallFile.Contains(StaticMethodMarshallGeneration));
Assert.IsTrue(generatedImplementationFile.Contains(StaticMethodImplementationGeneration));
}

[TestInitialize]
public void GenerateStubs()
{
var loadHints = new Dictionary<string, string>(StringComparer.Ordinal)
{
Expand All @@ -50,22 +214,22 @@ public void GeneratingStubsFromNFAppTest()

var fileToParse = TestObjectHelper.GenerationNFAppFullPath;
var fileToCompile = Path.ChangeExtension(fileToParse, "pe");

// get path where stubs will be generated
var stubPath = Path.Combine(
stubPath = Path.Combine(
TestObjectHelper.TestExecutionLocation,
"Stubs");

var assemblyDefinition = AssemblyDefinition.ReadAssembly(
fileToParse,
new ReaderParameters { AssemblyResolver = new LoadHintsAssemblyResolver(loadHints) });

var assemblyBuilder = new nanoAssemblyBuilder(assemblyDefinition, classNamesToExclude, false, false);
var assemblyBuilder = new nanoAssemblyBuilder(assemblyDefinition, classNamesToExclude, false);

using (var stream = File.Open(
Path.ChangeExtension(fileToCompile, "tmp"),
FileMode.Create,
FileAccess.ReadWrite))
Path.ChangeExtension(fileToCompile, "tmp"),
FileMode.Create,
FileAccess.ReadWrite))
using (var writer = new BinaryWriter(stream))
{
assemblyBuilder.Write(GetBinaryWriter(writer));
Expand All @@ -87,12 +251,11 @@ public void GeneratingStubsFromNFAppTest()
false);

skeletonGenerator.GenerateSkeleton();
}

// read generated stub file and look for the function declaration
string generatedFile = File.ReadAllText($"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.cpp");

Assert.IsTrue(generatedFile.Contains(NativeMethodGenerationDeclaration));

[TestCleanup]
public void DeleteStubs()
{
Directory.Delete(stubPath, true);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,11 @@ public void Method()

[MethodImpl(MethodImplOptions.InternalCall)]
private extern void NativeMethodWithReferenceParameters(ref byte refByteParam, ref ushort refUshortParam);

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void NativeStaticMethod();

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern byte NativeStaticMethodReturningByte(char charParam);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
<AssemblyName>StubsGenerationTestNFApp</AssemblyName>
<TargetFrameworkVersion>v1.0</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Label="nanoFramework">
<NFMDP_GENERATE_STUBS>True</NFMDP_GENERATE_STUBS>
</PropertyGroup>
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.props')" />
<ItemGroup>
<Compile Include="NativeMethodGeneration.cs" />
Expand Down

0 comments on commit 4da4bac

Please sign in to comment.