[runtime] Store assemblies' MVID in the generated static registrar code. (#15795)
This will increase app size a little bit: the space for the MVID + 4 bytes for each assembly, but we'll be able to validate and show a helpful error message if the generated static registrar code does not match the assembly loaded at runtime. It's also a step toward per-assembly static registration (ref: #12067).
This commit is contained in:
Родитель
9a52a6fbe7
Коммит
aa8ded8e51
|
@ -56,8 +56,8 @@ typedef struct {
|
|||
} MTProperty;
|
||||
|
||||
// This structure completely describes everything required to resolve a metadata token
|
||||
typedef struct MTFullTokenReference {
|
||||
const char *assembly_name; /* the name of the assembly */
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint32_t assembly_index; /* index into the 'assemblies' array in the registration map */
|
||||
uint32_t module_token;
|
||||
uint32_t token;
|
||||
} MTFullTokenReference;
|
||||
|
@ -99,10 +99,15 @@ typedef struct __attribute__((packed)) {
|
|||
const __unsafe_unretained Protocol * const * protocols; // the corresponding native protocols
|
||||
} MTProtocolMap;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
const char *name;
|
||||
const char *mvid;
|
||||
} MTAssembly;
|
||||
|
||||
struct MTRegistrationMap;
|
||||
|
||||
struct MTRegistrationMap {
|
||||
const char **assembly;
|
||||
const MTAssembly *assemblies;
|
||||
MTClassMap *map;
|
||||
const MTFullTokenReference *full_token_references;
|
||||
// There are some managed types that are not registered because their ObjC
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
@ -19,6 +20,10 @@ using Foundation;
|
|||
using Registrar;
|
||||
#endif
|
||||
|
||||
#if !COREBUILD
|
||||
using Xamarin.Bundler;
|
||||
#endif
|
||||
|
||||
#if !NET
|
||||
using NativeHandle = System.IntPtr;
|
||||
#endif
|
||||
|
@ -56,8 +61,8 @@ namespace ObjCRuntime {
|
|||
|
||||
|
||||
for (int i = 0; i < map->assembly_count; i++) {
|
||||
var ptr = Marshal.ReadIntPtr (map->assembly, i * IntPtr.Size);
|
||||
Runtime.Registrar.SetAssemblyRegistered (Marshal.PtrToStringAuto (ptr));
|
||||
var assembly = map->assemblies [i];
|
||||
Runtime.Registrar.SetAssemblyRegistered (Marshal.PtrToStringAuto (assembly.name));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,27 +297,27 @@ namespace ObjCRuntime {
|
|||
if ((token_reference & 0x1) == 0x1) {
|
||||
// full token reference
|
||||
var idx = (int) (token_reference >> 1);
|
||||
var entry = map->full_token_references + (IntPtr.Size + 8) * idx;
|
||||
var entry = map->full_token_references [idx];
|
||||
// first compare what's most likely to fail (the type's metadata token)
|
||||
var token = (uint) Marshal.ReadInt32 (entry + IntPtr.Size + 4);
|
||||
var token = entry.token;
|
||||
type_token |= 0x02000000 /* TypeDef - the token type is explicit in the full token reference, but not present in the type_token argument, so we have to add it before comparing */;
|
||||
if (type_token != token)
|
||||
return false;
|
||||
|
||||
// then the module token
|
||||
var module_token = (uint) Marshal.ReadInt32 (entry + IntPtr.Size);
|
||||
var module_token = entry.module_token;
|
||||
if (mod_token != module_token)
|
||||
return false;
|
||||
|
||||
// leave the assembly name for the end, since it's the most expensive comparison (string comparison)
|
||||
assembly_name = Marshal.ReadIntPtr (entry);
|
||||
assembly_name = map->assemblies [entry.assembly_index].name;
|
||||
} else {
|
||||
// packed token reference
|
||||
if (token_reference >> 8 != type_token)
|
||||
return false;
|
||||
|
||||
var assembly_index = (token_reference >> 1) & 0x7F;
|
||||
assembly_name = Marshal.ReadIntPtr (map->assembly, (int) assembly_index * IntPtr.Size);
|
||||
assembly_name = map->assemblies [(int) assembly_index].name;
|
||||
}
|
||||
|
||||
return Runtime.StringEquals (assembly_name, asm_name);
|
||||
|
@ -380,10 +385,11 @@ namespace ObjCRuntime {
|
|||
internal unsafe static MemberInfo? ResolveFullTokenReference (uint token_reference)
|
||||
{
|
||||
// sizeof (MTFullTokenReference) = IntPtr.Size + 4 + 4
|
||||
var entry = Runtime.options->RegistrationMap->full_token_references + (IntPtr.Size + 8) * (int) (token_reference >> 1);
|
||||
var assembly_name = Marshal.ReadIntPtr (entry);
|
||||
var module_token = (uint) Marshal.ReadInt32 (entry + IntPtr.Size);
|
||||
var token = (uint) Marshal.ReadInt32 (entry + IntPtr.Size + 4);
|
||||
var idx = (int) (token_reference >> 1);
|
||||
var entry = Runtime.options->RegistrationMap->full_token_references [idx];
|
||||
var assembly_name = Runtime.options->RegistrationMap->assemblies [entry.assembly_index].name;
|
||||
var module_token = entry.module_token;
|
||||
var token = entry.token;
|
||||
|
||||
#if LOG_TYPELOAD
|
||||
Console.WriteLine ($"ResolveFullTokenReference (0x{token_reference:X}) assembly name: {assembly_name} module token: 0x{module_token:X} token: 0x{token:X}.");
|
||||
|
@ -430,7 +436,7 @@ namespace ObjCRuntime {
|
|||
Console.WriteLine ($"ResolveTokenReference (0x{token_reference:X}) assembly index: {assembly_index} token: 0x{token:X}.");
|
||||
#endif
|
||||
|
||||
var assembly_name = Marshal.ReadIntPtr (map->assembly, (int) assembly_index * IntPtr.Size);
|
||||
var assembly_name = map->assemblies [(int) assembly_index].name;
|
||||
var assembly = ResolveAssembly (assembly_name);
|
||||
var module = ResolveModule (assembly, 0x1);
|
||||
|
||||
|
@ -474,7 +480,65 @@ namespace ObjCRuntime {
|
|||
throw ErrorHelper.CreateError (8020, $"Could not find the module with MetadataToken 0x{token:X} in the assembly {assembly}.");
|
||||
}
|
||||
|
||||
// Restrict this code to desktop for now, which is where most of the problems with outdated generated static registrar code occur.
|
||||
#if __MACOS__ || __MACCATALYST__
|
||||
static bool? verify_static_registrar_code;
|
||||
static object? verification_lock;
|
||||
static Dictionary<IntPtr, object?>? verified_assemblies; // Use Dictionary instead of HashSet to avoid pulling in System.Core.dll.
|
||||
unsafe static void VerifyStaticRegistrarCode (IntPtr assembly_name, Assembly assembly)
|
||||
{
|
||||
if (verify_static_registrar_code is null) {
|
||||
verify_static_registrar_code = !string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("XAMARIN_VALIDATE_STATIC_REGISTRAR_CODE"));
|
||||
verification_lock = new object ();
|
||||
}
|
||||
if (verify_static_registrar_code != true)
|
||||
return;
|
||||
|
||||
lock (verification_lock!) {
|
||||
if (verified_assemblies is null) {
|
||||
verified_assemblies = new Dictionary<IntPtr, object?> (Runtime.IntPtrEqualityComparer);
|
||||
} else if (verified_assemblies.ContainsKey (assembly_name)) {
|
||||
return;
|
||||
}
|
||||
verified_assemblies [assembly_name] = null;
|
||||
}
|
||||
|
||||
var map = Runtime.options->RegistrationMap;
|
||||
if (map is null)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < map->assembly_count; i++) {
|
||||
var entry = map->assemblies [i];
|
||||
var name = Marshal.PtrToStringAuto (entry.name)!;
|
||||
if (!Runtime.StringEquals (assembly_name, name))
|
||||
continue;
|
||||
try {
|
||||
var mvid = Marshal.PtrToStringAuto (entry.mvid)!;
|
||||
var runtime_mvid = assembly.ManifestModule.ModuleVersionId;
|
||||
var registered_mvid = Guid.Parse (mvid);
|
||||
if (registered_mvid == runtime_mvid)
|
||||
continue;
|
||||
throw ErrorHelper.CreateError (8044, Errors.MX8044 /* The assembly {0} has been modified since the app was built, invalidating the generated static registrar code. The MVID for the loaded assembly is {1}, while the MVID for the assembly the generated static registrar code corresponds to is {2}. */, name, runtime_mvid, registered_mvid);
|
||||
} catch (Exception e) {
|
||||
throw ErrorHelper.CreateError (8043, e, Errors.MX8043 /* An exception occurred while validating the static registrar code for {0}: {1} */, name, e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // __MACOS__ || __MACCATALYST__
|
||||
|
||||
static Assembly ResolveAssembly (IntPtr assembly_name)
|
||||
{
|
||||
if (TryResolveAssembly (assembly_name, out var asm)) {
|
||||
#if __MACOS__ || __MACCATALYST__
|
||||
VerifyStaticRegistrarCode (assembly_name, asm);
|
||||
#endif
|
||||
return asm;
|
||||
}
|
||||
|
||||
throw ErrorHelper.CreateError (8019, $"Could not find the assembly {Marshal.PtrToStringAuto (assembly_name)} in the loaded assemblies.");
|
||||
}
|
||||
|
||||
static bool TryResolveAssembly (IntPtr assembly_name, [NotNullWhen (true)] out Assembly? assembly)
|
||||
{
|
||||
// Find the assembly. We've already loaded all the assemblies that contain registered types, so just look at those assemblies.
|
||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies ()) {
|
||||
|
@ -482,12 +546,14 @@ namespace ObjCRuntime {
|
|||
continue;
|
||||
|
||||
#if LOG_TYPELOAD
|
||||
Console.WriteLine ($"ResolveAssembly (0x{assembly_name:X}): {asm.FullName}.");
|
||||
Console.WriteLine ($"TryResolveAssembly (0x{assembly_name:X}): {asm.FullName}.");
|
||||
#endif
|
||||
return asm;
|
||||
assembly = asm;
|
||||
return true;
|
||||
}
|
||||
|
||||
throw ErrorHelper.CreateError (8019, $"Could not find the assembly {Marshal.PtrToStringAuto (assembly_name)} in the loaded assemblies.");
|
||||
assembly = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal unsafe static uint GetTokenReference (Type type, bool throw_exception = true)
|
||||
|
@ -514,7 +580,7 @@ namespace ObjCRuntime {
|
|||
// Find the assembly index in our list of registered assemblies.
|
||||
int assembly_index = -1;
|
||||
for (int i = 0; i < map->assembly_count; i++) {
|
||||
var name_ptr = Marshal.ReadIntPtr (map->assembly, (int) i * IntPtr.Size);
|
||||
var name_ptr = map->assemblies [(int) i].name;
|
||||
if (Runtime.StringEquals (name_ptr, asm_name)) {
|
||||
assembly_index = i;
|
||||
break;
|
||||
|
@ -542,15 +608,16 @@ namespace ObjCRuntime {
|
|||
{
|
||||
var map = Runtime.options->RegistrationMap;
|
||||
for (int i = 0; i < map->full_token_reference_count; i++) {
|
||||
var ptr = map->full_token_references + (i * (IntPtr.Size + 8));
|
||||
var asm_ptr = Marshal.ReadIntPtr (ptr);
|
||||
var token = Marshal.ReadInt32 (ptr + IntPtr.Size + 4);
|
||||
var ftr = map->full_token_references [i];
|
||||
var token = ftr.token;
|
||||
if (token != metadata_token)
|
||||
continue;
|
||||
var mod_token = Marshal.ReadInt32 (ptr + IntPtr.Size);
|
||||
var mod_token = ftr.module_token;
|
||||
if (mod_token != module_token)
|
||||
continue;
|
||||
if (!Runtime.StringEquals (asm_ptr, assembly_name))
|
||||
var assembly_index = ftr.assembly_index;
|
||||
var assembly = map->assemblies [assembly_index];
|
||||
if (!Runtime.StringEquals (assembly.name, assembly_name))
|
||||
continue;
|
||||
|
||||
return ((uint) i << 1) + 1;
|
||||
|
|
|
@ -55,9 +55,9 @@ namespace ObjCRuntime {
|
|||
|
||||
#pragma warning disable 649 // Field 'X' is never assigned to, and will always have its default value
|
||||
internal unsafe struct MTRegistrationMap {
|
||||
public IntPtr assembly;
|
||||
public MTAssembly *assemblies;
|
||||
public MTClassMap *map;
|
||||
public IntPtr full_token_references; /* array of MTFullTokenReference */
|
||||
public MTFullTokenReference *full_token_references;
|
||||
public MTManagedClassMap* skipped_map;
|
||||
public MTProtocolWrapperMap* protocol_wrapper_map;
|
||||
public MTProtocolMap protocol_map;
|
||||
|
@ -78,6 +78,13 @@ namespace ObjCRuntime {
|
|||
UserType = 2,
|
||||
}
|
||||
|
||||
[StructLayout (LayoutKind.Sequential, Pack = 1)]
|
||||
internal unsafe struct MTFullTokenReference {
|
||||
public uint assembly_index;
|
||||
public uint module_token;
|
||||
public uint token;
|
||||
}
|
||||
|
||||
[StructLayout (LayoutKind.Sequential, Pack = 1)]
|
||||
internal struct MTClassMap {
|
||||
public IntPtr handle;
|
||||
|
@ -104,6 +111,12 @@ namespace ObjCRuntime {
|
|||
public IntPtr* protocols;
|
||||
}
|
||||
|
||||
[StructLayout (LayoutKind.Sequential, Pack = 1)]
|
||||
internal unsafe struct MTAssembly {
|
||||
public IntPtr name;
|
||||
public IntPtr mvid;
|
||||
}
|
||||
|
||||
/* Keep Delegates, Trampolines and InitializationOptions in sync with monotouch-glue.m */
|
||||
#pragma warning disable 649 // Field 'X' is never assigned to, and will always have its default value
|
||||
internal struct Trampolines {
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Foundation;
|
||||
using ObjCRuntime;
|
||||
|
||||
namespace MySimpleApp
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
static int Main (string[] args)
|
||||
{
|
||||
Console.WriteLine (Environment.GetEnvironmentVariable ("MAGIC_WORD"));
|
||||
|
||||
#if INCLUDED_ADDITIONAL_CODE
|
||||
GC.KeepAlive (typeof (AdditionalClass));
|
||||
#endif
|
||||
|
||||
return StaticRegistrarValidationTest ();
|
||||
}
|
||||
|
||||
static int StaticRegistrarValidationTest ()
|
||||
{
|
||||
try {
|
||||
using var obj = new SomeObj ();
|
||||
obj.Whatever ();
|
||||
xamarin_IntPtr_objc_msgSend_IntPtr_ref_IntPtr_exception (obj.Handle, Selector.GetHandle ("whatever"), IntPtr.Zero, IntPtr.Zero, out var gchandle);
|
||||
Console.WriteLine ($"GCH: {gchandle}");
|
||||
if (gchandle != IntPtr.Zero) {
|
||||
var gch = GCHandle.FromIntPtr (gchandle);
|
||||
var exc = (Exception) gch.Target;
|
||||
gch.Free ();
|
||||
throw exc;
|
||||
}
|
||||
return 1; // We're not supposed to get here
|
||||
} catch (Exception e) {
|
||||
Console.WriteLine ($"E: {e}");
|
||||
if (e.Message.Contains ("The assembly MyRegistrarApp has been modified since the app was built, invalidating the generated static registrar code."))
|
||||
return 0;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport ("__Internal")]
|
||||
static extern IntPtr xamarin_IntPtr_objc_msgSend_IntPtr_ref_IntPtr_exception (IntPtr handle, IntPtr selector, IntPtr p0, IntPtr p1, out IntPtr gchandle);
|
||||
}
|
||||
|
||||
public class SomeObj : NSObject
|
||||
{
|
||||
[Export ("whatever")]
|
||||
public IntPtr Whatever ()
|
||||
{
|
||||
return new IntPtr (0xdeadf00d);
|
||||
}
|
||||
}
|
||||
|
||||
public class DeadClass {} // Some code for the linker to remove
|
||||
|
||||
#if INCLUDED_ADDITIONAL_CODE
|
||||
public class AdditionalClass {
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
include ../shared.mk
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-maccatalyst</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\shared.csproj" />
|
||||
</Project>
|
|
@ -0,0 +1,2 @@
|
|||
TOP=../../..
|
||||
include $(TOP)/tests/common/shared-dotnet-test.mk
|
|
@ -0,0 +1 @@
|
|||
include ../shared.mk
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-ios</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\shared.csproj" />
|
||||
</Project>
|
|
@ -0,0 +1 @@
|
|||
include ../shared.mk
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-macos</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\shared.csproj" />
|
||||
</Project>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
|
||||
<ApplicationTitle>MyRegistrarApp</ApplicationTitle>
|
||||
<ApplicationId>com.xamarin.myregistrarapp</ApplicationId>
|
||||
|
||||
<ExcludeNUnitLiteReference>true</ExcludeNUnitLiteReference>
|
||||
<ExcludeTouchUnitReference>true</ExcludeTouchUnitReference>
|
||||
|
||||
<DefineConstants>$(DefineConstants);$(AdditionalDefineConstants)</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="../../common/shared-dotnet.csproj" />
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="../*.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,3 @@
|
|||
TOP=../../../..
|
||||
TESTNAME=MyRegistrarApp
|
||||
include $(TOP)/tests/common/shared-dotnet.mk
|
|
@ -0,0 +1 @@
|
|||
include ../shared.mk
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-tvos</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\shared.csproj" />
|
||||
</Project>
|
|
@ -34,7 +34,7 @@ class MainClass {
|
|||
|
||||
// Capture the current time
|
||||
var timestamp = DateTime.UtcNow;
|
||||
File.WriteAllText (mainFile, mainContents + "\n");
|
||||
File.WriteAllText (mainFile, mainContents);
|
||||
|
||||
// Build again
|
||||
rv = DotNet.AssertBuild (project_path, properties);
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
namespace Xamarin.Tests {
|
||||
[TestFixture]
|
||||
public class RegistrarTest : TestBaseClass
|
||||
{
|
||||
[TestCase (ApplePlatform.MacCatalyst, true)]
|
||||
[TestCase (ApplePlatform.MacOSX, true)]
|
||||
[TestCase (ApplePlatform.iOS, false)]
|
||||
[TestCase (ApplePlatform.TVOS, false)]
|
||||
public void InvalidStaticRegistrarValidation (ApplePlatform platform, bool validated)
|
||||
{
|
||||
var project = "MyRegistrarApp";
|
||||
var configuration = "Debug";
|
||||
var runtimeIdentifiers = GetDefaultRuntimeIdentifier (platform);
|
||||
Configuration.IgnoreIfIgnoredPlatform (platform);
|
||||
|
||||
var projectPath = GetProjectPath (project, platform: platform);
|
||||
Clean (projectPath);
|
||||
var properties = GetDefaultProperties ();
|
||||
properties ["Registrar"] = "static";
|
||||
// enable the linker (so that the main assembly is modified)
|
||||
properties ["LinkMode"] = "full";
|
||||
properties ["MtouchLink"] = "full";
|
||||
|
||||
DotNet.AssertBuild (projectPath, properties);
|
||||
|
||||
var appDir = GetAppPath (projectPath, platform, runtimeIdentifiers, configuration);
|
||||
var asmDir = Path.Combine (appDir, GetRelativeAssemblyDirectory (platform));
|
||||
|
||||
var appExecutable = Path.Combine (asmDir, project + ".dll");
|
||||
|
||||
// Save the first version of the main assembly in memory
|
||||
var firstAssembly = File.ReadAllBytes (appExecutable);
|
||||
|
||||
// Build again, including additional code
|
||||
properties ["AdditionalDefineConstants"] = "INCLUDED_ADDITIONAL_CODE";
|
||||
DotNet.AssertBuild (projectPath, properties);
|
||||
|
||||
// Revert to the original version of the main assembly
|
||||
File.WriteAllBytes (appExecutable, firstAssembly);
|
||||
|
||||
Environment.SetEnvironmentVariable ("XAMARIN_VALIDATE_STATIC_REGISTRAR_CODE", "1");
|
||||
try {
|
||||
if (validated) {
|
||||
ExecuteProjectWithMagicWordAndAssert (projectPath, platform, runtimeIdentifiers);
|
||||
} else if (CanExecute (platform, runtimeIdentifiers)) {
|
||||
var rv = base.Execute (GetNativeExecutable (platform, appDir), out var output, out _);
|
||||
Assert.AreEqual (1, rv.ExitCode, "Expected no validation");
|
||||
}
|
||||
} finally {
|
||||
Environment.SetEnvironmentVariable ("XAMARIN_VALIDATE_STATIC_REGISTRAR_CODE", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -40,9 +40,24 @@ namespace Xamarin.Tests {
|
|||
}
|
||||
|
||||
protected string GetAppPath (string projectPath, ApplePlatform platform, string runtimeIdentifiers, string configuration = "Debug")
|
||||
{
|
||||
return Path.Combine (GetBinDir (projectPath, platform, runtimeIdentifiers, configuration), Path.GetFileNameWithoutExtension (projectPath) + ".app");
|
||||
}
|
||||
|
||||
protected string GetBinDir (string projectPath, ApplePlatform platform, string runtimeIdentifiers, string configuration = "Debug")
|
||||
{
|
||||
return GetBinOrObjDir ("bin", projectPath, platform, runtimeIdentifiers, configuration);
|
||||
}
|
||||
|
||||
protected string GetObjDir (string projectPath, ApplePlatform platform, string runtimeIdentifiers, string configuration = "Debug")
|
||||
{
|
||||
return GetBinOrObjDir ("obj", projectPath, platform, runtimeIdentifiers, configuration);
|
||||
}
|
||||
|
||||
protected string GetBinOrObjDir (string binOrObj, string projectPath, ApplePlatform platform, string runtimeIdentifiers, string configuration = "Debug")
|
||||
{
|
||||
var appPathRuntimeIdentifier = runtimeIdentifiers.IndexOf (';') >= 0 ? "" : runtimeIdentifiers;
|
||||
return Path.Combine (Path.GetDirectoryName (projectPath)!, "bin", configuration, platform.ToFramework (), appPathRuntimeIdentifier, Path.GetFileNameWithoutExtension (projectPath) + ".app");
|
||||
return Path.Combine (Path.GetDirectoryName (projectPath)!, binOrObj, configuration, platform.ToFramework (), appPathRuntimeIdentifier);
|
||||
}
|
||||
|
||||
protected string GetOutputPath (string project, string? subdir, string runtimeIdentifiers, ApplePlatform platform, string configuration = "Debug")
|
||||
|
@ -275,20 +290,25 @@ namespace Xamarin.Tests {
|
|||
}
|
||||
|
||||
protected void ExecuteWithMagicWordAndAssert (string executable)
|
||||
{
|
||||
var rv = Execute (executable, out var output, out string magicWord);
|
||||
Assert.That (output.ToString (), Does.Contain (magicWord), "Contains magic word");
|
||||
Assert.AreEqual (0, rv.ExitCode, "ExitCode");
|
||||
}
|
||||
|
||||
protected Execution Execute (string executable, out StringBuilder output, out string magicWord)
|
||||
{
|
||||
if (!File.Exists (executable))
|
||||
throw new FileNotFoundException ($"The executable '{executable}' does not exists.");
|
||||
|
||||
var magicWord = Guid.NewGuid ().ToString ();
|
||||
magicWord = Guid.NewGuid ().ToString ();
|
||||
var env = new Dictionary<string, string?> {
|
||||
{ "MAGIC_WORD", magicWord },
|
||||
{ "DYLD_FALLBACK_LIBRARY_PATH", null }, // VSMac might set this, which may cause tests to crash.
|
||||
};
|
||||
|
||||
var output = new StringBuilder ();
|
||||
var rv = Execution.RunWithStringBuildersAsync (executable, Array.Empty<string> (), environment: env, standardOutput: output, standardError: output, timeout: TimeSpan.FromSeconds (15)).Result;
|
||||
Assert.That (output.ToString (), Does.Contain (magicWord), "Contains magic word");
|
||||
Assert.AreEqual (0, rv.ExitCode, "ExitCode");
|
||||
output = new StringBuilder ();
|
||||
return Execution.RunWithStringBuildersAsync (executable, Array.Empty<string> (), environment: env, standardOutput: output, standardError: output, timeout: TimeSpan.FromSeconds (15)).Result;
|
||||
}
|
||||
|
||||
public static StringBuilder AssertExecute (string executable, params string[] arguments)
|
||||
|
|
|
@ -2118,7 +2118,7 @@ namespace Registrar {
|
|||
|
||||
AutoIndentStringBuilder full_token_references = new AutoIndentStringBuilder ();
|
||||
uint full_token_reference_count;
|
||||
List<string> registered_assemblies = new List<string> ();
|
||||
List<(AssemblyDefinition Assembly, string Name)> registered_assemblies = new List<(AssemblyDefinition Assembly, string Name)> ();
|
||||
|
||||
bool IsPlatformType (TypeReference type)
|
||||
{
|
||||
|
@ -2837,9 +2837,9 @@ namespace Registrar {
|
|||
|
||||
if (string.IsNullOrEmpty (single_assembly)) {
|
||||
foreach (var assembly in GetAssemblies ())
|
||||
registered_assemblies.Add (GetAssemblyName (assembly));
|
||||
registered_assemblies.Add (new (assembly, GetAssemblyName (assembly)));
|
||||
} else {
|
||||
registered_assemblies.Add (single_assembly);
|
||||
registered_assemblies.Add (new (GetAssemblies ().Single (v => GetAssemblyName (v) == single_assembly), single_assembly));
|
||||
}
|
||||
|
||||
foreach (var @class in allTypes) {
|
||||
|
@ -3138,22 +3138,24 @@ namespace Registrar {
|
|||
map.AppendLine ();
|
||||
}
|
||||
|
||||
map.AppendLine ("static const char *__xamarin_registration_assemblies []= {");
|
||||
map.AppendLine ("static const MTAssembly __xamarin_registration_assemblies [] = {");
|
||||
int count = 0;
|
||||
foreach (var assembly in registered_assemblies) {
|
||||
count++;
|
||||
if (count > 1)
|
||||
map.AppendLine (", ");
|
||||
map.Append ("\"");
|
||||
map.Append (assembly);
|
||||
map.Append ("\"");
|
||||
map.Append ("{ \"");
|
||||
map.Append (assembly.Name);
|
||||
map.Append ("\", \"");
|
||||
map.Append (assembly.Assembly.MainModule.Mvid.ToString ());
|
||||
map.Append ("\" }");
|
||||
}
|
||||
map.AppendLine ();
|
||||
map.AppendLine ("};");
|
||||
map.AppendLine ();
|
||||
|
||||
if (full_token_reference_count > 0) {
|
||||
map.AppendLine ("static const struct MTFullTokenReference __xamarin_token_references [] = {");
|
||||
map.AppendLine ("static const MTFullTokenReference __xamarin_token_references [] = {");
|
||||
map.AppendLine (full_token_references);
|
||||
map.AppendLine ("};");
|
||||
map.AppendLine ();
|
||||
|
@ -4860,7 +4862,13 @@ namespace Registrar {
|
|||
default:
|
||||
throw ErrorHelper.CreateError (99, Errors.MX0099, $"unsupported tokentype ({member.MetadataToken.TokenType}) for {member.FullName}");
|
||||
}
|
||||
full_token_references.AppendFormat ("\t\t{{ /* #{3} = 0x{4:X} */ \"{0}\", 0x{1:X}, 0x{2:X} }},\n", GetAssemblyName (member.Module.Assembly), member.Module.MetadataToken.ToUInt32 (), member.MetadataToken.ToUInt32 (), full_token_reference_count, rv);
|
||||
var assemblyIndex = registered_assemblies.FindIndex (v => v.Assembly == member.Module.Assembly);
|
||||
var assemblyName = registered_assemblies [assemblyIndex].Name;
|
||||
var moduleToken = member.Module.MetadataToken.ToUInt32 ();
|
||||
var moduleName = member.Module.Name;
|
||||
var memberToken = member.MetadataToken.ToUInt32 ();
|
||||
var memberName = member.FullName;
|
||||
full_token_references.Append ($"\t\t{{ /* #{full_token_reference_count} = 0x{rv:X} */ {assemblyIndex} /* {assemblyName} */, 0x{moduleToken:X} /* {moduleName} */, 0x{memberToken:X} /* {memberName} */ }},\n");
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -4892,7 +4900,7 @@ namespace Registrar {
|
|||
|
||||
/* The assembly must be a registered one, and only within the first 128 assemblies */
|
||||
var assembly_name = GetAssemblyName (member.Module.Assembly);
|
||||
var index = registered_assemblies.IndexOf (assembly_name);
|
||||
var index = registered_assemblies.FindIndex (v => v.Name == assembly_name);
|
||||
if (index < 0 || index > 127)
|
||||
return CreateFullTokenReference (member);
|
||||
|
||||
|
|
|
@ -4113,5 +4113,23 @@ namespace Xamarin.Bundler {
|
|||
return ResourceManager.GetString("MX8042", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An exception occurred while validating the static registrar code for {0}: {1}.
|
||||
/// </summary>
|
||||
public static string MX8043 {
|
||||
get {
|
||||
return ResourceManager.GetString("MX8043", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The assembly {0} has been modified since the app was built, invalidating the generated static registrar code. The MVID for the loaded assembly is {1}, while the MVID for the assembly the generated static registrar code corresponds to is {2}..
|
||||
/// </summary>
|
||||
public static string MX8044 {
|
||||
get {
|
||||
return ResourceManager.GetString("MX8044", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2171,4 +2171,16 @@
|
|||
1: exception info
|
||||
</comment>
|
||||
</data>
|
||||
|
||||
<data name="MX8043" xml:space="preserve">
|
||||
<value>An exception occurred while validating the static registrar code for {0}: {1}</value>
|
||||
<comment>
|
||||
0: name of an assembly
|
||||
1: exception info
|
||||
</comment>
|
||||
</data>
|
||||
|
||||
<data name="MX8044" xml:space="preserve">
|
||||
<value>The assembly {0} has been modified since the app was built, invalidating the generated static registrar code. The MVID for the loaded assembly is {1}, while the MVID for the assembly the generated static registrar code corresponds to is {2}.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
Загрузка…
Ссылка в новой задаче