| // 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. |
| * @returns 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. |
| * @returns 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: []}]; |