Add support for debugging lazy-loaded assemblies (#25943)

* Add support for debugging lazy-loaded assemblies

* Address feedback from peer review

* Increase wait for output on tests
This commit is contained in:
Safia Abdalla 2020-09-22 15:23:39 +00:00 committed by GitHub
parent f66a1daeda
commit 93f73c7764
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 58 additions and 18 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -354,19 +354,42 @@ function createEmscriptenModuleInstance(resourceLoader: WebAssemblyResourceLoade
throw new Error(`${notMarked.join()} must be marked with 'BlazorWebAssemblyLazyLoad' item group in your project file to allow lazy-loading.`);
}
let pdbPromises: Promise<(ArrayBuffer | null)[]> | undefined;
if (hasDebuggingEnabled()) {
const pdbs = resourceLoader.bootConfig.resources.pdb;
const pdbsToLoad = assembliesMarkedAsLazy.map(a => changeExtension(a, '.pdb'))
if (pdbs) {
pdbPromises = Promise.all(pdbsToLoad
.map(pdb => lazyAssemblies.hasOwnProperty(pdb) ? resourceLoader.loadResource(pdb, `_framework/${pdb}`, lazyAssemblies[pdb], 'pdb') : null)
.map(async resource => resource ? (await resource.response).arrayBuffer() : null));
}
}
const resourcePromises = Promise.all(assembliesMarkedAsLazy
.map(assembly => resourceLoader.loadResource(assembly, `_framework/${assembly}`, lazyAssemblies[assembly], 'assembly'))
.map(async resource => (await resource.response).arrayBuffer()));
return BINDING.js_to_mono_obj(
resourcePromises.then(resourcesToLoad => {
Promise.all([resourcePromises, pdbPromises]).then(values => {
const resourcesToLoad = values[0];
const pdbsToLoad = values[1];
if (resourcesToLoad.length) {
window['Blazor']._internal.readLazyAssemblies = () => {
const array = BINDING.mono_obj_array_new(resourcesToLoad.length);
for (var i = 0; i < resourcesToLoad.length; i++) {
BINDING.mono_obj_array_set(array, i, BINDING.js_typed_array_to_array(new Uint8Array(resourcesToLoad[i])));
const assemblyBytes = BINDING.mono_obj_array_new(resourcesToLoad.length);
for (let i = 0; i < resourcesToLoad.length; i++) {
const assembly = resourcesToLoad[i] as ArrayBuffer;
BINDING.mono_obj_array_set(assemblyBytes, i, BINDING.js_typed_array_to_array(new Uint8Array(assembly)));
}
return array;
return assemblyBytes;
};
window['Blazor']._internal.readLazyPdbs = () => {
const pdbBytes = BINDING.mono_obj_array_new(resourcesToLoad.length);
for (let i = 0; i < resourcesToLoad.length; i++) {
const pdb = pdbsToLoad && pdbsToLoad[i] ? new Uint8Array(pdbsToLoad[i] as ArrayBufferLike) : new Uint8Array();
BINDING.mono_obj_array_set(pdbBytes, i, BINDING.js_typed_array_to_array(pdb));
}
return pdbBytes;
};
}

View File

@ -123,7 +123,12 @@ namespace Microsoft.NET.Sdk.BlazorWebAssembly
else if (string.Equals(extension, ".pdb", StringComparison.OrdinalIgnoreCase))
{
resourceData.pdb ??= new ResourceHashesByNameDictionary();
resourceList = resourceData.pdb;
if (IsLazyLoadedAssembly($"{fileName}.dll"))
{
resourceList = resourceData.lazyAssembly;
} else {
resourceList = resourceData.pdb;
}
}
else if (string.Equals(extension, ".dll", StringComparison.OrdinalIgnoreCase))
{

View File

@ -51,7 +51,7 @@ namespace Microsoft.AspNetCore.Builder
var processStartInfo = new ProcessStartInfo
{
FileName = muxerPath,
Arguments = $"exec \"{executablePath}\" --owner-pid {ownerPid} --DevToolsUrl {devToolsHost}",
Arguments = $"exec \"{executablePath}\" --OwnerPid {ownerPid} --DevToolsUrl {devToolsHost}",
UseShellExecute = false,
RedirectStandardOutput = true,
};

View File

@ -21,8 +21,9 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
/// </summary>
public sealed class LazyAssemblyLoader
{
internal const string GetDynamicAssemblies = "window.Blazor._internal.getLazyAssemblies";
internal const string ReadDynamicAssemblies = "window.Blazor._internal.readLazyAssemblies";
internal const string GetLazyAssemblies = "window.Blazor._internal.getLazyAssemblies";
internal const string ReadLazyAssemblies = "window.Blazor._internal.readLazyAssemblies";
internal const string ReadLazyPDBs = "window.Blazor._internal.readLazyPdbs";
private readonly IJSRuntime _jsRuntime;
private readonly HashSet<string> _loadedAssemblyCache;
@ -81,7 +82,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
var loadedAssemblies = new List<Assembly>();
var count = (int)await ((IJSUnmarshalledRuntime)_jsRuntime).InvokeUnmarshalled<string[], object, object, Task<object>>(
GetDynamicAssemblies,
GetLazyAssemblies,
newAssembliesToLoad.ToArray(),
null,
null);
@ -91,19 +92,29 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
return loadedAssemblies;
}
var assemblies = ((IJSUnmarshalledRuntime)_jsRuntime).InvokeUnmarshalled<object, object, object, object[]>(
ReadDynamicAssemblies,
var assemblies = ((IJSUnmarshalledRuntime)_jsRuntime).InvokeUnmarshalled<object, object, object, byte[][]>(
ReadLazyAssemblies,
null,
null,
null);
foreach (byte[] assembly in assemblies)
var pdbs = ((IJSUnmarshalledRuntime)_jsRuntime).InvokeUnmarshalled<object, object, object, byte[][]>(
ReadLazyPDBs,
null,
null,
null);
for (int i = 0; i < assemblies.Length; i++)
{
// The runtime loads assemblies into an isolated context by default. As a result,
// assemblies that are loaded via Assembly.Load aren't available in the app's context
// AKA the default context. To work around this, we explicitly load the assemblies
// into the default app context.
var loadedAssembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(assembly));
var assembly = assemblies[i];
var pdb = pdbs[i];
var loadedAssembly = pdb.Length == 0 ?
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(assembly)) :
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(assembly), new MemoryStream(pdb));
loadedAssemblies.Add(loadedAssembly);
_loadedAssemblyCache.Add(loadedAssembly.GetName().Name + ".dll");
}

View File

@ -90,15 +90,16 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
SetUrlViaPushState("/");
var app = Browser.MountTestComponent<TestRouterWithLazyAssembly>();
// Ensure that we haven't requested the lazy loaded assembly
// Ensure that we haven't requested the lazy loaded assembly or its PDB
Assert.False(HasLoadedAssembly("LazyTestContentPackage.dll"));
Assert.False(HasLoadedAssembly("LazyTestContentPackage.pdb"));
// Navigate to the designated route
SetUrlViaPushState("/WithLazyLoadedRoutes");
// Wait for the page to finish loading
new WebDriverWait(Browser, TimeSpan.FromSeconds(2)).Until(
driver => driver.FindElement(By.Id("lazy-load-msg")) != null);
driver => driver.FindElement(By.Id("lazy-load-msg")) != null);
// Now the assembly has been loaded
Assert.True(HasLoadedAssembly("LazyTestContentPackage.dll"));