|
|
|
@ -196,7 +196,8 @@ namespace Avalonia.Build.Tasks |
|
|
|
var runtimeHelpers = typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.XamlIlRuntimeHelpers"); |
|
|
|
var createRootServiceProviderMethod = asm.MainModule.ImportReference( |
|
|
|
typeSystem.GetTypeReference(runtimeHelpers).Resolve().Methods |
|
|
|
.First(x => x.Name == "CreateRootServiceProviderV2")); |
|
|
|
.First(x => x.Name == "CreateRootServiceProviderV3")); |
|
|
|
var serviceProviderType = createRootServiceProviderMethod.ReturnType; |
|
|
|
|
|
|
|
var loaderDispatcherDef = new TypeDefinition(CompiledAvaloniaXamlNamespace, "!XamlLoader", |
|
|
|
TypeAttributes.Class | TypeAttributes.Public, asm.MainModule.TypeSystem.Object); |
|
|
|
@ -212,11 +213,36 @@ namespace Avalonia.Build.Tasks |
|
|
|
MethodAttributes.Static | MethodAttributes.Public, |
|
|
|
asm.MainModule.TypeSystem.Object) |
|
|
|
{ |
|
|
|
Parameters = {new ParameterDefinition(asm.MainModule.TypeSystem.String)} |
|
|
|
Parameters = |
|
|
|
{ |
|
|
|
new ParameterDefinition(serviceProviderType), |
|
|
|
new ParameterDefinition(asm.MainModule.TypeSystem.String) |
|
|
|
}, |
|
|
|
}; |
|
|
|
var loaderDispatcherMethodOld = new MethodDefinition("TryLoad", |
|
|
|
MethodAttributes.Static | MethodAttributes.Public, |
|
|
|
asm.MainModule.TypeSystem.Object) |
|
|
|
{ |
|
|
|
Parameters = |
|
|
|
{ |
|
|
|
new ParameterDefinition(asm.MainModule.TypeSystem.String) |
|
|
|
}, |
|
|
|
Body = |
|
|
|
{ |
|
|
|
Instructions = |
|
|
|
{ |
|
|
|
Instruction.Create(OpCodes.Ldnull), |
|
|
|
Instruction.Create(OpCodes.Ldarg_0), |
|
|
|
Instruction.Create(OpCodes.Call, loaderDispatcherMethod), |
|
|
|
Instruction.Create(OpCodes.Ret) |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
loaderDispatcherDef.Methods.Add(loaderDispatcherMethod); |
|
|
|
loaderDispatcherDef.Methods.Add(loaderDispatcherMethodOld); |
|
|
|
asm.MainModule.Types.Add(loaderDispatcherDef); |
|
|
|
|
|
|
|
|
|
|
|
var stringEquals = asm.MainModule.ImportReference(asm.MainModule.TypeSystem.String.Resolve().Methods.First( |
|
|
|
m => |
|
|
|
m.IsStatic && m.Name == "Equals" && m.Parameters.Count == 3 && |
|
|
|
@ -377,30 +403,42 @@ namespace Avalonia.Build.Tasks |
|
|
|
classTypeDefinition.Fields.Add(designLoaderField); |
|
|
|
|
|
|
|
const string TrampolineName = "!XamlIlPopulateTrampoline"; |
|
|
|
var trampoline = new MethodDefinition(TrampolineName, |
|
|
|
MethodAttributes.Static | MethodAttributes.Private, asm.MainModule.TypeSystem.Void); |
|
|
|
trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition)); |
|
|
|
classTypeDefinition.Methods.Add(trampoline); |
|
|
|
var trampolineMethodWithoutSP = new Lazy<MethodDefinition>(() => CreateTrampolineMethod(false)); |
|
|
|
var trampolineMethodWithSP = new Lazy<MethodDefinition>(() => CreateTrampolineMethod(true)); |
|
|
|
MethodDefinition CreateTrampolineMethod(bool hasSystemProviderArg) |
|
|
|
{ |
|
|
|
var trampoline = new MethodDefinition(TrampolineName, |
|
|
|
MethodAttributes.Static | MethodAttributes.Private, asm.MainModule.TypeSystem.Void); |
|
|
|
if (hasSystemProviderArg) |
|
|
|
{ |
|
|
|
trampoline.Parameters.Add(new ParameterDefinition(serviceProviderType)); |
|
|
|
} |
|
|
|
trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition)); |
|
|
|
|
|
|
|
classTypeDefinition.Methods.Add(trampoline); |
|
|
|
|
|
|
|
var regularStart = Instruction.Create(OpCodes.Call, createRootServiceProviderMethod); |
|
|
|
var regularStart = Instruction.Create(OpCodes.Nop); |
|
|
|
|
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Brfalse, regularStart)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, designLoaderLoad)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Brfalse, regularStart)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(hasSystemProviderArg ? OpCodes.Ldarg_1 : OpCodes.Ldarg_0)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, designLoaderLoad)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); |
|
|
|
|
|
|
|
trampoline.Body.Instructions.Add(regularStart); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, compiledPopulateMethod)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); |
|
|
|
CopyDebugDocument(trampoline, compiledPopulateMethod); |
|
|
|
trampoline.Body.Instructions.Add(regularStart); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(hasSystemProviderArg ? OpCodes.Ldarg_0 : OpCodes.Ldnull)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, createRootServiceProviderMethod)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(hasSystemProviderArg ? OpCodes.Ldarg_1 : OpCodes.Ldarg_0)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, compiledPopulateMethod)); |
|
|
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); |
|
|
|
CopyDebugDocument(trampoline, compiledPopulateMethod); |
|
|
|
return trampoline; |
|
|
|
} |
|
|
|
|
|
|
|
var foundXamlLoader = false; |
|
|
|
// Find AvaloniaXamlLoader.Load(this) and replace it with !XamlIlPopulateTrampoline(this)
|
|
|
|
foreach (var method in classTypeDefinition.Methods |
|
|
|
.Where(m => !m.Attributes.HasFlag(MethodAttributes.Static))) |
|
|
|
// Find AvaloniaXamlLoader.Load(this) or AvaloniaXamlLoader.Load(sp, this) and replace it with !XamlIlPopulateTrampoline(this)
|
|
|
|
foreach (var method in classTypeDefinition.Methods.ToArray()) |
|
|
|
{ |
|
|
|
var i = method.Body.Instructions; |
|
|
|
for (var c = 1; c < i.Count; c++) |
|
|
|
@ -422,7 +460,20 @@ namespace Avalonia.Build.Tasks |
|
|
|
{ |
|
|
|
if (MatchThisCall(i, c - 1)) |
|
|
|
{ |
|
|
|
i[c].Operand = trampoline; |
|
|
|
i[c].Operand = trampolineMethodWithoutSP.Value; |
|
|
|
foundXamlLoader = true; |
|
|
|
} |
|
|
|
} |
|
|
|
if (op != null |
|
|
|
&& op.Name == "Load" |
|
|
|
&& op.Parameters.Count == 2 |
|
|
|
&& op.Parameters[0].ParameterType.FullName == "System.IServiceProvider" |
|
|
|
&& op.Parameters[1].ParameterType.FullName == "System.Object" |
|
|
|
&& op.DeclaringType.FullName == "Avalonia.Markup.Xaml.AvaloniaXamlLoader") |
|
|
|
{ |
|
|
|
if (MatchThisCall(i, c - 1)) |
|
|
|
{ |
|
|
|
i[c].Operand = trampolineMethodWithSP.Value; |
|
|
|
foundXamlLoader = true; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -439,7 +490,7 @@ namespace Avalonia.Build.Tasks |
|
|
|
{ |
|
|
|
var i = ctors[0].Body.Instructions; |
|
|
|
var retIdx = i.IndexOf(i.Last(x => x.OpCode == OpCodes.Ret)); |
|
|
|
i.Insert(retIdx, Instruction.Create(OpCodes.Call, trampoline)); |
|
|
|
i.Insert(retIdx, Instruction.Create(OpCodes.Call, trampolineMethodWithoutSP.Value)); |
|
|
|
i.Insert(retIdx, Instruction.Create(OpCodes.Ldarg_0)); |
|
|
|
} |
|
|
|
else |
|
|
|
@ -461,20 +512,33 @@ namespace Avalonia.Build.Tasks |
|
|
|
null : |
|
|
|
classTypeDefinition.GetConstructors().FirstOrDefault(c => |
|
|
|
c.IsPublic && !c.IsStatic && !c.HasParameters); |
|
|
|
var constructorWithSp = compiledBuildMethod != null ? |
|
|
|
null : |
|
|
|
classTypeDefinition.GetConstructors().FirstOrDefault(c => |
|
|
|
c.IsPublic && !c.IsStatic && c.Parameters.Count == 1 && c.Parameters[0].ParameterType.FullName == serviceProviderType.FullName); |
|
|
|
|
|
|
|
if (compiledBuildMethod != null || parameterlessConstructor != null) |
|
|
|
if (compiledBuildMethod != null || parameterlessConstructor != null || constructorWithSp != null) |
|
|
|
{ |
|
|
|
var i = loaderDispatcherMethod.Body.Instructions; |
|
|
|
var nop = Instruction.Create(OpCodes.Nop); |
|
|
|
i.Add(Instruction.Create(OpCodes.Ldarg_0)); |
|
|
|
i.Add(Instruction.Create(OpCodes.Ldarg_1)); |
|
|
|
i.Add(Instruction.Create(OpCodes.Ldstr, res.Uri)); |
|
|
|
i.Add(Instruction.Create(OpCodes.Ldc_I4, (int)StringComparison.OrdinalIgnoreCase)); |
|
|
|
i.Add(Instruction.Create(OpCodes.Call, stringEquals)); |
|
|
|
i.Add(Instruction.Create(OpCodes.Brfalse, nop)); |
|
|
|
if (parameterlessConstructor != null) |
|
|
|
{ |
|
|
|
i.Add(Instruction.Create(OpCodes.Newobj, parameterlessConstructor)); |
|
|
|
} |
|
|
|
else if (constructorWithSp != null) |
|
|
|
{ |
|
|
|
i.Add(Instruction.Create(OpCodes.Ldarg_0)); |
|
|
|
i.Add(Instruction.Create(OpCodes.Call, createRootServiceProviderMethod)); |
|
|
|
i.Add(Instruction.Create(OpCodes.Newobj, constructorWithSp)); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
i.Add(Instruction.Create(OpCodes.Ldarg_0)); |
|
|
|
i.Add(Instruction.Create(OpCodes.Call, createRootServiceProviderMethod)); |
|
|
|
i.Add(Instruction.Create(OpCodes.Call, compiledBuildMethod)); |
|
|
|
} |
|
|
|
@ -482,6 +546,11 @@ namespace Avalonia.Build.Tasks |
|
|
|
i.Add(Instruction.Create(OpCodes.Ret)); |
|
|
|
i.Add(nop); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
engine.LogWarning(BuildEngineErrorCode.Loader, "", |
|
|
|
$"XAML resource \"{res.Uri}\" won't be reachable via runtime loader, as no public constructor was found"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|