Skip to content

Commit

Permalink
(#354) CodeGen: add integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ForNeVeR committed Nov 19, 2023
1 parent 3259db0 commit 10d1cd7
Show file tree
Hide file tree
Showing 20 changed files with 175 additions and 64 deletions.
1 change: 1 addition & 0 deletions Cesium.CodeGen.Tests/Cesium.CodeGen.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

<ItemGroup>
<ProjectReference Include="..\Cesium.CodeGen\Cesium.CodeGen.csproj" />
<ProjectReference Include="..\Cesium.Compiler\Cesium.Compiler.csproj" />
<ProjectReference Include="..\Cesium.Core\Cesium.Core.csproj" />
<ProjectReference Include="..\Cesium.Parser\Cesium.Parser.csproj" />
<ProjectReference Include="..\Cesium.Runtime\Cesium.Runtime.csproj" />
Expand Down
46 changes: 40 additions & 6 deletions Cesium.CodeGen.Tests/CodeGenNetInteropTests.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System.Diagnostics.CodeAnalysis;
using Cesium.Compiler;
using Cesium.Test.Framework;
using Xunit.Abstractions;

namespace Cesium.CodeGen.Tests;

// TODO: Make them run in parallel, as all the integration tests
public class CodeGenNetInteropTests : CodeGenTestBase
{
private readonly ITestOutputHelper _output;
Expand All @@ -21,8 +23,40 @@ private async Task DoTest(
_output,
CSharpCompilationUtil.DefaultRuntime,
cSharpCode);
var cesiumAssembly = GenerateAssembly(runtime: null, arch: architecture, sources: new[]{cCode}, referencePaths: new[] { cSharpAssemblyPath });
var (cesiumAssembly, assemblyContents) = GenerateAssembly(runtime: null, arch: architecture, sources: new[]{cCode}, referencePaths: new[] { cSharpAssemblyPath });
await VerifyTypes(cesiumAssembly, architecture);
await VerifyAssemblyRuns(assemblyContents.ToArray(), cSharpAssemblyPath);
}

private async Task VerifyAssemblyRuns(byte[] assemblyContentToRun, string referencePath)
{
var testDirectoryPath = Path.GetTempFileName();
File.Delete(testDirectoryPath);
Directory.CreateDirectory(testDirectoryPath);

try
{
var assemblyPath = Path.Combine(testDirectoryPath, "EntryPoint.dll");
var runtimeConfigPath = Path.ChangeExtension(assemblyPath, ".runtimeconfig.json");

await File.WriteAllBytesAsync(assemblyPath, assemblyContentToRun);
await File.WriteAllTextAsync(runtimeConfigPath, RuntimeConfig.EmitNet7());

DeployReferenceAssembly(CSharpCompilationUtil.CesiumRuntimeLibraryPath);
DeployReferenceAssembly(referencePath);

await ExecUtil.RunToSuccess(_output, "dotnet", testDirectoryPath, new[] { assemblyPath });
}
finally
{
Directory.Delete(testDirectoryPath, recursive: true);
}

void DeployReferenceAssembly(string assemblyPath)
{
var targetFilePath = Path.Combine(testDirectoryPath, Path.GetFileName(assemblyPath));
File.Copy(assemblyPath, targetFilePath);
}
}

[Theory]
Expand All @@ -41,7 +75,7 @@ public Task PointerInterop(TargetArchitectureSet architecture) => DoTest(
int main(void)
{
int x = 0;
return Func(&x);
return Func(&x) - 1;
}
""");

Expand All @@ -62,7 +96,7 @@ public static class Test
int main(void)
{
int x = 0;
return Func(&x);
return Func(&x) - 1;
}
""");

Expand All @@ -83,7 +117,7 @@ public static class Test
int main(void)
{
int x = 0;
return Func(&x);
return Func(&x) - 1;
}
""");

Expand All @@ -108,7 +142,7 @@ int myFunc()
int main(void)
{
return Func(&myFunc);
return Func(&myFunc) - 1;
}
""");

Expand All @@ -133,7 +167,7 @@ int myFunc()
int main(void)
{
return Func(&myFunc);
return Func(&myFunc) - 1;
}
""");
}
30 changes: 21 additions & 9 deletions Cesium.CodeGen.Tests/CodeGenTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,28 @@ namespace Cesium.CodeGen.Tests;
[UseInvariantCulture]
public abstract class CodeGenTestBase : VerifyTestBase
{
protected static AssemblyDefinition GenerateAssembly(TargetRuntimeDescriptor? runtime, params string[] sources) =>
GenerateAssembly(sources, runtime, TargetArchitectureSet.Dynamic, "", "", Array.Empty<string>());
protected static AssemblyDefinition GenerateAssembly(TargetRuntimeDescriptor? runtime, params string[] sources)
{
var (assembly, _) = GenerateAssembly(
sources,
runtime,
@namespace: "",
globalTypeFqn: "",
referencePaths: Array.Empty<string>());
return assembly;
}

protected static AssemblyDefinition GenerateAssembly(
TargetRuntimeDescriptor? runtime,
TargetArchitectureSet arch = TargetArchitectureSet.Dynamic,
string @namespace = "",
string globalTypeFqn = "", params string[] sources) =>
GenerateAssembly(sources, runtime, arch, @namespace, globalTypeFqn, Array.Empty<string>());
string globalTypeFqn = "", params string[] sources)
{
var (assembly, _) = GenerateAssembly(sources, runtime, arch, @namespace, globalTypeFqn, Array.Empty<string>());
return assembly;
}

protected static AssemblyDefinition GenerateAssembly(
protected static (AssemblyDefinition, byte[]) GenerateAssembly(
string[] sources,
TargetRuntimeDescriptor? runtime = null,
TargetArchitectureSet arch = TargetArchitectureSet.Dynamic,
Expand Down Expand Up @@ -104,15 +116,15 @@ private static void GenerateCode(AssemblyContext context, IEnumerable<string> so
}
}

private static AssemblyDefinition EmitAssembly(AssemblyContext context)
private static (AssemblyDefinition, byte[]) EmitAssembly(AssemblyContext context)
{
var assembly = context.VerifyAndGetAssembly();

// To resolve IL labels:
using var stream = new MemoryStream();
assembly.Write(stream);

return assembly;
return (assembly, stream.ToArray());
}

[MustUseReturnValue]
Expand All @@ -138,7 +150,7 @@ protected static Task VerifyMethods(TypeDefinition type, params object[] paramet
}

[MustUseReturnValue]
protected static Task VerifyMethods(IEnumerable<TypeDefinition> types, params object[] parameters)
protected static Task VerifyMethods(IEnumerable<TypeDefinition?> types, params object[] parameters)
{
var result = new StringBuilder();
int i = 0;
Expand All @@ -149,7 +161,7 @@ protected static Task VerifyMethods(IEnumerable<TypeDefinition> types, params ob
result.AppendLine();
}

DumpMethods(type, result);
DumpMethods(type!, result);
i++;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
IL_0004: conv.i
IL_0005: call Cesium.Runtime.CPtr`1<T> Cesium.Runtime.CPtr`1<System.Int32>::op_Implicit(T*)
IL_000a: call System.Int32 Test::Func(Cesium.Runtime.CPtr`1<System.Int32>)
IL_000f: ret
IL_000f: ldc.i4.1
IL_0010: sub
IL_0011: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
IL_0004: conv.i
IL_0005: call Cesium.Runtime.CPtr`1<T> Cesium.Runtime.CPtr`1<System.Int32>::op_Implicit(T*)
IL_000a: call System.Int32 Test::Func(Cesium.Runtime.CPtr`1<System.Int32>)
IL_000f: ret
IL_000f: ldc.i4.1
IL_0010: sub
IL_0011: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@
IL_0001: ret

System.Int32 <Module>::main()
IL_0000: ldftn System.Int32 <Module>::myFunc()
IL_0006: conv.i
IL_0007: newobj Cesium.Runtime.FuncPtr`1<TDelegate> Cesium.Runtime.FuncPtr`1<System.Func`1<System.Int32>>::.ctor(System.Void*)
IL_000c: call System.Int32 Test::Func(Cesium.Runtime.FuncPtr`1<System.Func`1<System.Int32>>)
IL_0011: ret
Locals:
Cesium.Runtime.FuncPtr`1<System.Func`1<System.Int32>> V_0
IL_0000: ldloca V_0
IL_0004: ldftn System.Int32 <Module>::myFunc()
IL_000a: conv.i
IL_000b: call System.Int32 Test::Func(Cesium.Runtime.FuncPtr`1<System.Func`1<System.Int32>>)
IL_0010: ldc.i4.1
IL_0011: sub
IL_0012: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@
IL_0001: ret

System.Int32 <Module>::main()
IL_0000: ldftn System.Int32 <Module>::myFunc()
IL_0006: conv.i
IL_0007: newobj Cesium.Runtime.FuncPtr`1<TDelegate> Cesium.Runtime.FuncPtr`1<System.Func`1<System.Int32>>::.ctor(System.Void*)
IL_000c: call System.Int32 Test::Func(Cesium.Runtime.FuncPtr`1<System.Func`1<System.Int32>>)
IL_0011: ret
Locals:
Cesium.Runtime.FuncPtr`1<System.Func`1<System.Int32>> V_0
IL_0000: ldloca V_0
IL_0004: ldftn System.Int32 <Module>::myFunc()
IL_000a: conv.i
IL_000b: call System.Int32 Test::Func(Cesium.Runtime.FuncPtr`1<System.Func`1<System.Int32>>)
IL_0010: ldc.i4.1
IL_0011: sub
IL_0012: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
System.Int32 <Module>::main()
IL_0000: ldftn System.Int32 <Module>::myFunc()
IL_0006: call System.Int32 Test::Func(method System.Int32 *())
IL_000b: ret
IL_000b: ldc.i4.1
IL_000c: sub
IL_000d: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
System.Int32 <Module>::main()
IL_0000: ldftn System.Int32 <Module>::myFunc()
IL_0006: call System.Int32 Test::Func(method System.Int32 *())
IL_000b: ret
IL_000b: ldc.i4.1
IL_000c: sub
IL_000d: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
IL_0001: stloc.0
IL_0002: ldloca.s V_0
IL_0004: call System.Int32 Test::Func(System.Int32*)
IL_0009: ret
IL_0009: ldc.i4.1
IL_000a: sub
IL_000b: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
IL_0001: stloc.0
IL_0002: ldloca.s V_0
IL_0004: call System.Int32 Test::Func(System.Int32*)
IL_0009: ret
IL_0009: ldc.i4.1
IL_000a: sub
IL_000b: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
IL_0004: conv.i
IL_0005: call Cesium.Runtime.VoidPtr Cesium.Runtime.VoidPtr::op_Implicit(System.Void*)
IL_000a: call System.Int32 Test::Func(Cesium.Runtime.VoidPtr)
IL_000f: ret
IL_000f: ldc.i4.1
IL_0010: sub
IL_0011: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
IL_0004: conv.i
IL_0005: call Cesium.Runtime.VoidPtr Cesium.Runtime.VoidPtr::op_Implicit(System.Void*)
IL_000a: call System.Int32 Test::Func(Cesium.Runtime.VoidPtr)
IL_000f: ret
IL_000f: ldc.i4.1
IL_0010: sub
IL_0011: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
Expand Down
12 changes: 10 additions & 2 deletions Cesium.CodeGen/Contexts/AssemblyContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,21 @@ TypeDefinition GetRuntimeType(string typeName) =>
throw new AssertException($"Could not find type {typeName} in the runtime assembly.");

_runtimeCPtr = Module.ImportReference(GetRuntimeType(TypeSystemEx.CPtrFullTypeName));
_cPtrConverterCache = new ConversionMethodCache(_runtimeCPtr, "op_Implicit", Module);
_cPtrConverterCache = new ConversionMethodCache(
_runtimeCPtr,
ReturnType: _runtimeCPtr.MakeGenericInstanceType(_runtimeCPtr.GenericParameters.Single()),
"op_Implicit",
Module);

RuntimeVoidPtr = Module.ImportReference(GetRuntimeType(TypeSystemEx.VoidPtrFullTypeName));
_voidPtrConverter = new(() => GetImplicitCastOperator(TypeSystemEx.VoidPtrFullTypeName));

_runtimeFuncPtr = Module.ImportReference(GetRuntimeType(TypeSystemEx.FuncPtrFullTypeName));
_funcPtrConstructorCache = new ConversionMethodCache(_runtimeFuncPtr, ".ctor", Module);
_funcPtrConstructorCache = new ConversionMethodCache(
_runtimeFuncPtr,
ReturnType: null,
".ctor",
Module);

_importedActionDelegates = new("System", "Action", Module);
_importedFuncDelegates = new("System", "Func", Module);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace Cesium.CodeGen.Contexts.Utilities;
/// </remarks>
internal record ConversionMethodCache(
TypeReference GenericType,
TypeReference? ReturnType,
string MethodName,
ModuleDefinition TargetModule)
{
Expand All @@ -33,17 +34,11 @@ private MethodReference FindMethod(TypeReference argument)
{
var genericMethod = GenericType.Resolve().Methods.Single(m => m.Name == MethodName);
var declaringType = GenericType.MakeGenericInstanceType(argument);
var methodReference = new MethodReference(
genericMethod.Name,
returnType: GenericType.MakeGenericInstanceType(
GenericType.GenericParameters.Single()),
declaringType);
foreach (var p in genericMethod.Parameters)
{
methodReference.Parameters.Add(
new ParameterDefinition(p.Name, p.Attributes, p.ParameterType));
}
var methodReference = TargetModule.ImportReference(genericMethod);
methodReference.DeclaringType = declaringType;
if (ReturnType != null)
methodReference.ReturnType = ReturnType;

return TargetModule.ImportReference(methodReference);
return methodReference;
}
}
11 changes: 6 additions & 5 deletions Cesium.CodeGen/Ir/Expressions/TypeCastExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ public TypeCastExpression(CastExpression castExpression)

public void EmitTo(IEmitScope scope)
{
if (TargetType is InteropType iType)
{
iType.EmitConversion(scope, Expression);
return;
}

Expression.EmitTo(scope);

var ts = scope.CTypeSystem;
Expand Down Expand Up @@ -56,11 +62,6 @@ public void EmitTo(IEmitScope scope)
Add(OpCodes.Conv_R8);
else if (TargetType is PointerType || TargetType.Equals(ts.NativeInt) || TargetType.Equals(ts.NativeUInt))
Add(OpCodes.Conv_I);
else if (TargetType is InteropType iType)
{
Add(OpCodes.Conv_I); // TODO: Should only emit if required.
scope.Method.Body.Instructions.Add(iType.GetConvertInstruction(scope.AssemblyContext));
}
else
throw new AssertException($"Type {TargetType} is not supported.");

Expand Down
Loading

0 comments on commit 10d1cd7

Please sign in to comment.