chrome-devtools-frontend
Version:
Chrome DevTools UI
118 lines (105 loc) • 4.79 kB
text/typescript
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {globMatch} from './GlobMatch.js';
/**
* A path substitution specifies a string prefix pattern to be
* replaced with a new string. This is the pendant of the
* `set substitute-path old new` feature that is found in GDB
* and `settings set target.source-map old new` feature that
* is found in LLDB.
*/
export interface PathSubstitution {
readonly from: string; readonly to: string;
}
/**
* List of {@type PathSubstitution}s.
*/
export type PathSubstitutions = readonly PathSubstitution[];
/**
* Resolve a source path (as stored in DWARF debugging information) to an absolute URL.
*
* Note that we treat "." specially as a pattern, since LLDB normalizes paths before
* returning them from the DWARF parser. Our logic replicates the logic found in the
* LLDB frontend in `PathMappingList::RemapPath()` inside `Target/PathMappingList.cpp`
* (http://cs/github/llvm/llvm-project/lldb/source/Target/PathMappingList.cpp?l=157-185).
*
* @param pathSubstitutions possible substitutions to apply to the {@param sourcePath}, applies the first match.
* @param sourcePath the source path as found in the debugging information.
* @param baseURL the URL of the WebAssembly module, which is used to resolve relative source paths.
* @return an absolute `file:`-URI or a URL relative to the {@param baseURL}.
*/
export function resolveSourcePathToURL(pathSubstitutions: PathSubstitutions, sourcePath: string, baseURL: URL): URL {
// Normalize '\' to '/' in sourcePath first.
let resolvedSourcePath = sourcePath.replace(/\\/g, '/');
// Apply source path substitutions first.
for (const {from, to} of pathSubstitutions) {
if (resolvedSourcePath.startsWith(from)) {
resolvedSourcePath = to + resolvedSourcePath.slice(from.length);
break;
}
// Relative paths won't have a leading "./" in them unless "." is the only
// thing in the relative path so we need to work around "." carefully.
if (from === '.') {
// We need to figure whether sourcePath can be considered a relative path,
// ruling out absolute POSIX and Windows paths, as well as file:, http: and
// https: URLs.
if (!resolvedSourcePath.startsWith('/') && !/^([A-Z]|file|https?):/i.test(resolvedSourcePath)) {
resolvedSourcePath = `${to}/${resolvedSourcePath}`;
break;
}
}
}
if (resolvedSourcePath.startsWith('/')) {
if (resolvedSourcePath.startsWith('//')) {
return new URL(`file:${resolvedSourcePath}`);
}
return new URL(`file://${resolvedSourcePath}`);
}
if (/^[A-Z]:/i.test(resolvedSourcePath)) {
return new URL(`file:/${resolvedSourcePath}`);
}
return new URL(resolvedSourcePath, baseURL.href);
}
/**
* Configuration for locating source files for a given WebAssembly module.
* If the name is `undefined`, the configuration is the default configuration,
* which is chosen if there's no named configuration matching the basename of
* the WebAssembly module file.
* The name can be a wildcard pattern, and {@see globMatch} will be used to
* match the name against the URL of the WebAssembly module file.
*/
export interface ModuleConfiguration {
readonly name?: string; readonly pathSubstitutions: PathSubstitutions;
}
/**
* List of {@type ModuleConfiguration}s. These lists are intended to have
* a default configuration, whose name field is `undefined`, which is chosen
* when no matching named configuration is found.
*/
export type ModuleConfigurations = readonly ModuleConfiguration[];
/**
* Locate the configuration for a given `something.wasm` module file name.
*
* @param moduleConfigurations list of module configurations to scan.
* @param moduleName the URL of the module to lookup.
* @return the matching module configuration or the default fallback.
*/
export function findModuleConfiguration(
moduleConfigurations: ModuleConfigurations, moduleURL: URL): ModuleConfiguration {
let defaultModuleConfiguration: ModuleConfiguration = {pathSubstitutions: []};
for (const moduleConfiguration of moduleConfigurations) {
// The idea here is that module configurations will have at most
// one default configuration, so picking the last here is fine.
if (moduleConfiguration.name === undefined) {
defaultModuleConfiguration = moduleConfiguration;
continue;
}
// Perform wildcard pattern matching on the full URL.
if (globMatch(moduleConfiguration.name, moduleURL.href)) {
return moduleConfiguration;
}
}
return defaultModuleConfiguration;
}
export const DEFAULT_MODULE_CONFIGURATIONS: ModuleConfigurations = [{pathSubstitutions: []}];