mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-31 05:40:43 +08:00
feat(hle): prefer loaded guest exports for selected registrations (#844)
Co-authored-by: Acelogic <miguelc4600@gmail.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using SharpEmu.Core.Cpu.Native;
|
||||
using SharpEmu.HLE;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpEmu.Libs.Tests.Cpu;
|
||||
|
||||
public sealed class DirectExecutionBackendLlePreferenceTests
|
||||
{
|
||||
[Fact]
|
||||
public void ExplicitLlePreference_AllowsNonKernelRegisteredExport()
|
||||
{
|
||||
var export = Export("libSceNpCppWebApi", preferLle: true);
|
||||
|
||||
Assert.True(DirectExecutionBackend.ShouldResolveRegisteredExportViaLle(
|
||||
export,
|
||||
preferLleForLibc: false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExplicitLlePreference_CannotOverrideKernelHleBoundary()
|
||||
{
|
||||
var export = Export("libKernel", preferLle: true);
|
||||
|
||||
Assert.False(DirectExecutionBackend.ShouldResolveRegisteredExportViaLle(
|
||||
export,
|
||||
preferLleForLibc: true));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RegisteredExportWithoutLlePreference_RemainsHle()
|
||||
{
|
||||
var export = Export("libSceNpCppWebApi", preferLle: false);
|
||||
|
||||
Assert.False(DirectExecutionBackend.ShouldResolveRegisteredExportViaLle(
|
||||
export,
|
||||
preferLleForLibc: false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExistingLibcPolicy_CanStillSelectRegisteredFirmwareExport()
|
||||
{
|
||||
var export = Export("libSceLibcInternal", preferLle: false);
|
||||
|
||||
Assert.True(DirectExecutionBackend.ShouldResolveRegisteredExportViaLle(
|
||||
export,
|
||||
preferLleForLibc: true));
|
||||
}
|
||||
|
||||
private static ExportedFunction Export(string libraryName, bool preferLle) =>
|
||||
new(
|
||||
libraryName,
|
||||
"Zxa0VhQVTsk",
|
||||
"sceKernelWaitSema",
|
||||
Generation.Gen5,
|
||||
static _ => 0,
|
||||
preferLle);
|
||||
}
|
||||
@@ -53,6 +53,40 @@ public sealed class SysAbiExportAnalyzerTests
|
||||
AssertSingle(diagnostics, "SHEM001");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateNidOnTheSameMultiAttributeHandlerIsReported()
|
||||
{
|
||||
var diagnostics = Analyze("""
|
||||
using SharpEmu.HLE;
|
||||
|
||||
public static class Exports
|
||||
{
|
||||
[SysAbiExport(Nid = "Zxa0VhQVTsk", ExportName = "sceKernelWaitSema")]
|
||||
[SysAbiExport(Nid = "Zxa0VhQVTsk", ExportName = "sceKernelWaitSema")]
|
||||
public static int Shared(CpuContext ctx) => 0;
|
||||
}
|
||||
""");
|
||||
|
||||
AssertSingle(diagnostics, "SHEM001");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EveryAttributeOnAMultiAttributeHandlerIsAnalyzed()
|
||||
{
|
||||
var diagnostics = Analyze("""
|
||||
using SharpEmu.HLE;
|
||||
|
||||
public static class Exports
|
||||
{
|
||||
[SysAbiExport(Nid = "Zxa0VhQVTsk", ExportName = "sceKernelWaitSema")]
|
||||
[SysAbiExport(Nid = "not_a_nid")]
|
||||
public static int Shared(CpuContext ctx) => 0;
|
||||
}
|
||||
""");
|
||||
|
||||
AssertSingle(diagnostics, "SHEM002");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MalformedNidIsReported()
|
||||
{
|
||||
|
||||
@@ -36,6 +36,11 @@ public sealed class SysAbiExportGeneratorTests
|
||||
// Guest string marshalling: the thunk reads the pointer before the handler.
|
||||
[SysAbiExport(Nid = "1G3lF1Gg1k8", ExportName = "sceKernelOpen")]
|
||||
public static int KernelOpen(CpuContext ctx, [GuestCString(4096)] string path, int flags) => 0;
|
||||
|
||||
// A single fail-closed handler may back a catalog of LLE-preferred exports.
|
||||
[SysAbiExport(Nid = "5fbPUzoA2fM", ExportName = "sceLleFirst", Target = Generation.Gen5, LibraryName = "libSceLle", PreferLle = true)]
|
||||
[SysAbiExport(Nid = "L9NfM+f4f1Y", ExportName = "sceLleSecond", Target = Generation.Gen5, LibraryName = "libSceLle", PreferLle = true)]
|
||||
public static int LleFallback(CpuContext ctx) => -1;
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -123,6 +128,22 @@ public sealed class SysAbiExportGeneratorTests
|
||||
Assert.Contains("(target & registrationGeneration) == 0", generated, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultipleLlePreferredAttributesShareOneFailClosedHandler()
|
||||
{
|
||||
var (_, generated) = RoslynTestHost.RunGenerator(RoslynTestHost.Compile(HandlerSource));
|
||||
|
||||
Assert.Contains("\"5fbPUzoA2fM\"", generated, StringComparison.Ordinal);
|
||||
Assert.Contains("\"L9NfM+f4f1Y\"", generated, StringComparison.Ordinal);
|
||||
Assert.Equal(
|
||||
2,
|
||||
generated.Split("global::TestExports.SampleExports.LleFallback", StringSplitOptions.None).Length - 1);
|
||||
Assert.Contains(
|
||||
", true, global::TestExports.SampleExports.LleFallback",
|
||||
generated,
|
||||
StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AssemblyWithoutExportsEmitsNoRegistry()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user