Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 102 additions & 15 deletions lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import * as path from "path";
import * as util from "util";
import * as vscode from "vscode";

import { LLDBDapOptions } from "./types";

/**
Expand All @@ -8,15 +11,7 @@ import { LLDBDapOptions } from "./types";
export class LLDBDapDescriptorFactory
implements vscode.DebugAdapterDescriptorFactory
{
private lldbDapOptions: LLDBDapOptions;

constructor(lldbDapOptions: LLDBDapOptions) {
this.lldbDapOptions = lldbDapOptions;
}

static async isValidDebugAdapterPath(
pathUri: vscode.Uri,
): Promise<Boolean> {
static async isValidFile(pathUri: vscode.Uri): Promise<Boolean> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this just be a function outside of the class?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're also calling this from extension.ts. I guess we could export the function but that doesn't seem much better than leaving it a static method?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is doing anything specific for LLDBDapDescriptorFactory and a top level isValidExe or isExecutable is straight forward to reason vs LLDBDapDescriptorFactory.isValidFile.

try {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to validate that the file is executable. It looks like the VS Code FS API doesn't have that info but we could use the node FS instead, something like:

import { access, constants } from 'node:fs/promises';

try {
  await access('<path>', constants.X_OK);
  console.log('can execute');
} catch {
  console.error('cannot execute');
} 

const fileStats = await vscode.workspace.fs.stat(pathUri);
if (!(fileStats.type & vscode.FileType.File)) {
Expand All @@ -28,6 +23,70 @@ export class LLDBDapDescriptorFactory
return true;
}

static async findDAPExecutable(): Promise<string | undefined> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is really not complex but a bit long. Can you split it into smaller chunks?

let executable = "lldb-dap";
if (process.platform === "win32") {
executable = "lldb-dap.exe";
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just use the ternary comparator


// Prefer lldb-dap from Xcode on Darwin.
if (process.platform === "darwin") {
try {
const exec = util.promisify(require("child_process").execFile);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't use require. Just import child process in the regular way, and then pass it to promisify. That way the typechecker will work correctly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be moved to the top of the file? Its never modified after creation.

let { stdout, stderr } = await exec("/usr/bin/xcrun", [
"-find",
executable,
]);
if (stdout) {
return stdout.toString().trimEnd();
}
} catch (error) {}
}

// Find lldb-dap in the user's path.
let env_path =
process.env["PATH"] ||
(process.platform === "win32" ? process.env["Path"] : null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
process.env["PATH"] ||
(process.platform === "win32" ? process.env["Path"] : null);
process.platform === "win32" ? process.env["Path"] : process.env["PATH"]

if (!env_path) {
return undefined;
}

const paths = env_path.split(path.delimiter);
for (const p of paths) {
const exe_path = path.join(p, executable);
if (
await LLDBDapDescriptorFactory.isValidFile(vscode.Uri.file(exe_path))
) {
return exe_path;
}
}

return undefined;
}

static async getDAPExecutable(
session: vscode.DebugSession,
): Promise<string | undefined> {
const config = vscode.workspace.getConfiguration(
"lldb-dap",
session.workspaceFolder,
);

// Prefer the explicitly specified path in the extension's configuration.
const configPath = config.get<string>("executable-path");
if (configPath && configPath.length !== 0) {
return configPath;
}

// Try finding the lldb-dap binary.
const foundPath = await LLDBDapDescriptorFactory.findDAPExecutable();
if (foundPath) {
return foundPath;
}

return undefined;
}

async createDebugAdapterDescriptor(
session: vscode.DebugSession,
executable: vscode.DebugAdapterExecutable | undefined,
Expand All @@ -36,14 +95,42 @@ export class LLDBDapDescriptorFactory
"lldb-dap",
session.workspaceFolder,
);
const customPath = config.get<string>("executable-path");
const path: string = customPath || executable!!.command;

const fileUri = vscode.Uri.file(path);
if (!(await LLDBDapDescriptorFactory.isValidDebugAdapterPath(fileUri))) {
LLDBDapDescriptorFactory.showLLDBDapNotFoundMessage(fileUri.path);
const log_path = config.get<string>("log-path");
let env: { [key: string]: string } = {};
if (log_path) {
env["LLDBDAP_LOG"] = log_path;
}
const configEnvironment =
config.get<{ [key: string]: string }>("environment") || {};
const dapPath = await LLDBDapDescriptorFactory.getDAPExecutable(session);
const dbgOptions = {
env: {
...executable?.options?.env,
...configEnvironment,
...env,
},
};
if (dapPath) {
const fileUri = vscode.Uri.file(dapPath);
if (!(await LLDBDapDescriptorFactory.isValidFile(fileUri))) {
LLDBDapDescriptorFactory.showLLDBDapNotFoundMessage(fileUri.path);
return undefined;
}
return new vscode.DebugAdapterExecutable(dapPath, [], dbgOptions);
} else if (executable) {
const fileUri = vscode.Uri.file(executable.command);
if (!(await LLDBDapDescriptorFactory.isValidFile(fileUri))) {
LLDBDapDescriptorFactory.showLLDBDapNotFoundMessage(fileUri.path);
return undefined;
}
return new vscode.DebugAdapterExecutable(
executable.command,
executable.args,
dbgOptions,
);
}
return this.lldbDapOptions.createDapExecutableCommand(session, executable);
return undefined;
}

/**
Expand Down
76 changes: 10 additions & 66 deletions lldb/tools/lldb-dap/src-ts/extension.ts
Original file line number Diff line number Diff line change
@@ -1,74 +1,22 @@
import * as path from "path";
import * as util from "util";
import * as vscode from "vscode";
import { LLDBDapOptions } from "./types";
import { DisposableContext } from "./disposable-context";
import { LLDBDapDescriptorFactory } from "./debug-adapter-factory";

/**
* This creates the configurations for this project if used as a standalone
* extension.
*/
function createDefaultLLDBDapOptions(): LLDBDapOptions {
return {
debuggerType: "lldb-dap",
async createDapExecutableCommand(
session: vscode.DebugSession,
packageJSONExecutable: vscode.DebugAdapterExecutable | undefined,
): Promise<vscode.DebugAdapterExecutable | undefined> {
const config = vscode.workspace.getConfiguration(
"lldb-dap",
session.workspaceFolder,
);
const path = config.get<string>("executable-path");
const log_path = config.get<string>("log-path");

let env: { [key: string]: string } = {};
if (log_path) {
env["LLDBDAP_LOG"] = log_path;
}
const configEnvironment = config.get<{ [key: string]: string }>("environment") || {};
if (path) {
const dbgOptions = {
env: {
...configEnvironment,
...env,
}
};
return new vscode.DebugAdapterExecutable(path, [], dbgOptions);
} else if (packageJSONExecutable) {
return new vscode.DebugAdapterExecutable(
packageJSONExecutable.command,
packageJSONExecutable.args,
{
...packageJSONExecutable.options,
env: {
...packageJSONExecutable.options?.env,
...configEnvironment,
...env,
},
},
);
} else {
return undefined;
}
},
};
}
import { LLDBDapDescriptorFactory } from "./debug-adapter-factory";
import { DisposableContext } from "./disposable-context";
import { LLDBDapOptions } from "./types";

/**
* This class represents the extension and manages its life cycle. Other extensions
* using it as as library should use this class as the main entry point.
*/
export class LLDBDapExtension extends DisposableContext {
private lldbDapOptions: LLDBDapOptions;

constructor(lldbDapOptions: LLDBDapOptions) {
constructor() {
super();
this.lldbDapOptions = lldbDapOptions;

this.pushSubscription(
vscode.debug.registerDebugAdapterDescriptorFactory(
this.lldbDapOptions.debuggerType,
new LLDBDapDescriptorFactory(this.lldbDapOptions),
"lldb-dap",
new LLDBDapDescriptorFactory(),
),
);

Expand All @@ -81,9 +29,7 @@ export class LLDBDapExtension extends DisposableContext {

if (dapPath) {
const fileUri = vscode.Uri.file(dapPath);
if (
await LLDBDapDescriptorFactory.isValidDebugAdapterPath(fileUri)
) {
if (await LLDBDapDescriptorFactory.isValidFile(fileUri)) {
return;
}
}
Expand All @@ -98,7 +44,5 @@ export class LLDBDapExtension extends DisposableContext {
* This is the entry point when initialized by VS Code.
*/
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
new LLDBDapExtension(createDefaultLLDBDapOptions()),
);
context.subscriptions.push(new LLDBDapExtension());
}
Loading