Public dry run dryrun-f1e9b7b784b4
Source commit: f1e9b7b784b4a4dd28799719681535e2a4ef44c9
This commit is contained in:
parent
20c72e6066
commit
8c0c336ae9
5 changed files with 1829 additions and 20 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -305,7 +305,7 @@ fn launch_threads(entry: &str) -> BTreeMap<i64, VirtualThread> {
|
|||
thread(MAIN_THREAD, "main", &format!("{entry} virtual process"), 12),
|
||||
thread(LINUX_THREAD, "compile-linux", "compile linux", 42),
|
||||
thread(WINDOWS_THREAD, "compile-windows", "compile windows", 52),
|
||||
thread(PACKAGE_THREAD, "package", "package artifacts", 64),
|
||||
thread(PACKAGE_THREAD, "package-release", "package artifacts", 64),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|thread| (thread.id, thread))
|
||||
|
|
@ -1428,6 +1428,7 @@ fn parse_let_binding_name(line: &str) -> Option<String> {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
enum SourceLocalRuntimeValue {
|
||||
SourceSnapshot(String),
|
||||
TaskHandle {
|
||||
task: TaskId,
|
||||
thread_id: i64,
|
||||
|
|
@ -1441,6 +1442,9 @@ enum SourceLocalRuntimeValue {
|
|||
impl SourceLocalRuntimeValue {
|
||||
fn display(&self, _state: &AdapterState) -> String {
|
||||
match self {
|
||||
SourceLocalRuntimeValue::SourceSnapshot(digest) => {
|
||||
format!("SourceSnapshot {{ digest = \"{digest}\" }}")
|
||||
}
|
||||
SourceLocalRuntimeValue::TaskHandle {
|
||||
task,
|
||||
thread_id,
|
||||
|
|
@ -1462,7 +1466,18 @@ fn infer_disasmer_source_local_value(
|
|||
statement: &str,
|
||||
runtime_values: &BTreeMap<String, SourceLocalRuntimeValue>,
|
||||
) -> Option<SourceLocalRuntimeValue> {
|
||||
if let Some(task_function) = extract_call_argument(statement, "disasmer::spawn::task(") {
|
||||
if statement.contains("prepare_source_snapshot()") {
|
||||
return Some(SourceLocalRuntimeValue::SourceSnapshot(
|
||||
source_snapshot_digest_from_project(state).unwrap_or_else(|| {
|
||||
format!(
|
||||
"source://local-checkout/{}",
|
||||
project_snapshot_suffix(&state.project)
|
||||
)
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(task_function) = extract_spawn_task_function(statement) {
|
||||
let task_name = extract_string_argument(statement, ".name(");
|
||||
let spawned_thread =
|
||||
thread_for_spawn_statement(state, &task_function, task_name.as_deref())?;
|
||||
|
|
@ -1498,12 +1513,83 @@ fn infer_disasmer_source_local_value(
|
|||
None
|
||||
}
|
||||
|
||||
fn extract_call_argument(statement: &str, marker: &str) -> Option<String> {
|
||||
fn extract_spawn_task_function(statement: &str) -> Option<String> {
|
||||
if let Some(args) = extract_call_arguments(statement, "disasmer::spawn::task(") {
|
||||
return args.first().cloned();
|
||||
}
|
||||
let args = extract_call_arguments(statement, "disasmer::spawn::task_with_arg(")?;
|
||||
args.get(1).cloned()
|
||||
}
|
||||
|
||||
fn source_snapshot_digest_from_project(state: &AdapterState) -> Option<String> {
|
||||
let source =
|
||||
fs::read_to_string(resolve_source_path(&state.project, &state.source_path)).ok()?;
|
||||
let digest_marker = source.find("digest:")?;
|
||||
let after_digest = &source[digest_marker..];
|
||||
let first_quote = after_digest.find('"')? + 1;
|
||||
let rest = &after_digest[first_quote..];
|
||||
let end_quote = rest.find('"')?;
|
||||
Some(rest[..end_quote].to_owned())
|
||||
}
|
||||
|
||||
fn extract_call_arguments(statement: &str, marker: &str) -> Option<Vec<String>> {
|
||||
let start = statement.find(marker)? + marker.len();
|
||||
let rest = &statement[start..];
|
||||
let end = rest.find(')')?;
|
||||
let value = rest[..end].trim();
|
||||
(!value.is_empty()).then_some(value.to_owned())
|
||||
let mut args = Vec::new();
|
||||
let mut current = String::new();
|
||||
let mut depth = 0_i32;
|
||||
let mut in_string = false;
|
||||
let mut escaped = false;
|
||||
|
||||
for ch in rest.chars() {
|
||||
if in_string {
|
||||
current.push(ch);
|
||||
if escaped {
|
||||
escaped = false;
|
||||
} else if ch == '\\' {
|
||||
escaped = true;
|
||||
} else if ch == '"' {
|
||||
in_string = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
match ch {
|
||||
'"' => {
|
||||
in_string = true;
|
||||
current.push(ch);
|
||||
}
|
||||
'(' | '[' | '{' => {
|
||||
depth += 1;
|
||||
current.push(ch);
|
||||
}
|
||||
')' => {
|
||||
if depth == 0 {
|
||||
let value = current.trim();
|
||||
if !value.is_empty() {
|
||||
args.push(value.to_owned());
|
||||
}
|
||||
return (!args.is_empty()).then_some(args);
|
||||
}
|
||||
depth -= 1;
|
||||
current.push(ch);
|
||||
}
|
||||
']' | '}' => {
|
||||
depth -= 1;
|
||||
current.push(ch);
|
||||
}
|
||||
',' if depth == 0 => {
|
||||
let value = current.trim();
|
||||
if !value.is_empty() {
|
||||
args.push(value.to_owned());
|
||||
}
|
||||
current.clear();
|
||||
}
|
||||
_ => current.push(ch),
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn extract_string_argument(statement: &str, marker: &str) -> Option<String> {
|
||||
|
|
@ -1518,9 +1604,15 @@ fn extract_env_name(statement: &str) -> Option<String> {
|
|||
if statement.contains("windows_env") || statement.contains("env!(\"windows\")") {
|
||||
return Some("windows".to_owned());
|
||||
}
|
||||
if statement.contains("linux_command_env") || statement.contains("env!(\"linux-command\")") {
|
||||
return Some("linux-command".to_owned());
|
||||
}
|
||||
if statement.contains("linux_env") || statement.contains("env!(\"linux\")") {
|
||||
return Some("linux".to_owned());
|
||||
}
|
||||
if statement.contains("coordinator_env") || statement.contains("env!(\"coordinator\")") {
|
||||
return Some("coordinator".to_owned());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
|
@ -1552,25 +1644,27 @@ fn thread_for_spawn_statement<'a>(
|
|||
}
|
||||
}
|
||||
let normalized = task_function.replace('_', "-");
|
||||
state
|
||||
.threads
|
||||
.values()
|
||||
.find(|thread| thread.task.as_str() == normalized)
|
||||
state.threads.values().find(|thread| {
|
||||
thread.task.as_str() == normalized
|
||||
|| (thread.id == PACKAGE_THREAD && normalized == "package-release")
|
||||
})
|
||||
}
|
||||
|
||||
fn task_environment(thread: &VirtualThread) -> &'static str {
|
||||
match thread.id {
|
||||
WINDOWS_THREAD => "windows",
|
||||
MAIN_THREAD => "coordinator",
|
||||
PACKAGE_THREAD => "coordinator",
|
||||
_ => "linux",
|
||||
}
|
||||
}
|
||||
|
||||
fn task_arguments_value(thread: &VirtualThread) -> String {
|
||||
if thread.task.as_str() == "task_add_one" {
|
||||
"[input: i32]".to_owned()
|
||||
} else {
|
||||
"[]".to_owned()
|
||||
match thread.task.as_str() {
|
||||
"compile-linux" => "[source: SourceSnapshot]".to_owned(),
|
||||
"package-release" => "[inputs: Vec<Artifact>]".to_owned(),
|
||||
"task_add_one" => "[input: i32]".to_owned(),
|
||||
_ => "[]".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2224,6 +2318,7 @@ fn read_message<R: BufRead>(reader: &mut R) -> Result<Option<Value>> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs;
|
||||
use std::io::Cursor;
|
||||
|
||||
use serde_json::json;
|
||||
|
|
@ -2349,7 +2444,7 @@ mod tests {
|
|||
let error = freeze_all(&mut state, LINUX_THREAD, None).unwrap_err();
|
||||
|
||||
assert_eq!(error.thread_id, PACKAGE_THREAD);
|
||||
assert_eq!(error.task, TaskId::from("package"));
|
||||
assert_eq!(error.task, TaskId::from("package-release"));
|
||||
assert!(error.message().contains("could not freeze"));
|
||||
assert_eq!(state.epoch, 0);
|
||||
assert!(state
|
||||
|
|
@ -2541,6 +2636,119 @@ mod tests {
|
|||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn source_locals_infer_task_with_arg_values_from_runtime_state() {
|
||||
let project =
|
||||
std::env::temp_dir().join(format!("disasmer-dap-task-with-arg-{}", std::process::id()));
|
||||
let src = project.join("src");
|
||||
fs::create_dir_all(&src).unwrap();
|
||||
fs::write(
|
||||
src.join("main.rs"),
|
||||
r#"use disasmer::{Artifact, EnvRef, SourceSnapshot};
|
||||
|
||||
async fn build_release() -> Result<(), disasmer::TaskArgError> {
|
||||
let source = prepare_source_snapshot();
|
||||
|
||||
let compile = disasmer::spawn::task_with_arg(source.clone(), compile_linux)
|
||||
.name("compile linux")
|
||||
.env(linux_command_env())
|
||||
.start()
|
||||
.await?;
|
||||
|
||||
let compile_thread = compile.virtual_thread_id();
|
||||
let linux_artifact = compile.join().await;
|
||||
|
||||
let package = disasmer::spawn::task_with_arg(vec![linux_artifact.clone()], package_release)
|
||||
.name("package release")
|
||||
.env(coordinator_env())
|
||||
.start()
|
||||
.await?;
|
||||
|
||||
let package_thread = package.virtual_thread_id();
|
||||
let release_artifact = package.join().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_source_snapshot() -> SourceSnapshot {
|
||||
SourceSnapshot {
|
||||
digest: "source://coordinator/quick-test-checkout".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn linux_command_env() -> EnvRef {
|
||||
disasmer::env!("linux-command")
|
||||
}
|
||||
|
||||
fn coordinator_env() -> EnvRef {
|
||||
disasmer::env!("coordinator")
|
||||
}
|
||||
|
||||
fn compile_linux(source: SourceSnapshot) -> Artifact {
|
||||
Artifact { id: source.digest }
|
||||
}
|
||||
|
||||
fn package_release(inputs: Vec<Artifact>) -> Artifact {
|
||||
inputs.into_iter().next().unwrap()
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut state = AdapterState::default();
|
||||
state.project = project.to_string_lossy().into_owned();
|
||||
state.source_path = "src/main.rs".to_owned();
|
||||
state.threads.get_mut(&MAIN_THREAD).unwrap().line = 23;
|
||||
let thread = state.threads[&MAIN_THREAD].clone();
|
||||
|
||||
let locals = variables_response(&state, thread.locals_ref);
|
||||
let locals = locals["variables"].as_array().unwrap();
|
||||
|
||||
assert!(locals.iter().any(|variable| {
|
||||
variable["name"] == "source"
|
||||
&& variable["value"]
|
||||
.as_str()
|
||||
.is_some_and(|value| value.contains("source://coordinator/quick-test-checkout"))
|
||||
}));
|
||||
assert!(locals.iter().any(|variable| {
|
||||
variable["name"] == "compile"
|
||||
&& variable["value"].as_str().is_some_and(|value| {
|
||||
value.contains("TaskHandle")
|
||||
&& value.contains("compile-linux")
|
||||
&& value.contains("virtual_thread_id = 2")
|
||||
&& value.contains("linux-command")
|
||||
})
|
||||
}));
|
||||
assert!(locals
|
||||
.iter()
|
||||
.any(|variable| variable["name"] == "compile_thread" && variable["value"] == "2"));
|
||||
assert!(locals.iter().any(|variable| {
|
||||
variable["name"] == "linux_artifact"
|
||||
&& variable["value"]
|
||||
.as_str()
|
||||
.is_some_and(|value| value.contains("Artifact"))
|
||||
}));
|
||||
assert!(locals.iter().any(|variable| {
|
||||
variable["name"] == "package"
|
||||
&& variable["value"].as_str().is_some_and(|value| {
|
||||
value.contains("TaskHandle")
|
||||
&& value.contains("package-release")
|
||||
&& value.contains("virtual_thread_id = 4")
|
||||
&& value.contains("coordinator")
|
||||
})
|
||||
}));
|
||||
assert!(locals
|
||||
.iter()
|
||||
.any(|variable| variable["name"] == "package_thread" && variable["value"] == "4"));
|
||||
assert!(locals.iter().any(|variable| {
|
||||
variable["name"] == "release_artifact"
|
||||
&& variable["value"]
|
||||
.as_str()
|
||||
.is_some_and(|value| value.contains("Artifact"))
|
||||
}));
|
||||
|
||||
let _ = fs::remove_dir_all(project);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wasm_frame_locals_expose_wasmtime_runtime_values() {
|
||||
let mut state = AdapterState::default();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue