From 39f4154563999f4bddfe9be2029d354e7323c8a5 Mon Sep 17 00:00:00 2001 From: Guillaume Anthouard Date: Mon, 12 Sep 2022 23:55:16 +0200 Subject: [PATCH] Inspector links --- hlbc-gui/src/main.rs | 49 ++++++--------- hlbc-gui/src/views/inspector.rs | 103 +++++++++++++++++++++----------- 2 files changed, 86 insertions(+), 66 deletions(-) diff --git a/hlbc-gui/src/main.rs b/hlbc-gui/src/main.rs index 5b315af..56dd770 100644 --- a/hlbc-gui/src/main.rs +++ b/hlbc-gui/src/main.rs @@ -1,8 +1,8 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release -use std::cell::{Ref, RefCell, RefMut}; +use std::cell::{Cell, Ref, RefCell, RefMut}; use std::io::BufReader; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::rc::Rc; use std::{env, fs}; @@ -88,7 +88,7 @@ struct App { impl eframe::App for App { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { { - if let Some(tab) = self.ctx.as_ref().and_then(|app| app.new_tab()) { + if let Some(tab) = self.ctx.as_ref().and_then(|app| app.take_tab_to_open()) { self.tree[NodeIndex::root().left()].append_tab(tab); } } @@ -179,57 +179,46 @@ impl eframe::App for App { /// Usage warning ! The methods 'lock' the inner RefCell immutably and mutably (RW lock). /// Be careful of guards (Ref<> and RefMut<>) lifetimes. #[derive(Clone)] -struct AppCtxHandle(Rc>); +struct AppCtxHandle(Rc); impl AppCtxHandle { fn new(appctx: AppCtx) -> Self { - Self(Rc::new(RefCell::new(appctx))) + Self(Rc::new(appctx)) } - fn lock(&self) -> Ref { - self.0.borrow() + fn file(&self) -> &Path { + &self.0.file } - /// mut lock - fn lock_mut(&self) -> RefMut { - self.0.borrow_mut() - } - - fn file(&self) -> Ref { - Ref::map(self.lock(), |app| &app.file) - } - - fn code(&self) -> Ref { - Ref::map(self.lock(), |app| &app.code) + fn code(&self) -> &Bytecode { + &self.0.code } /// mut lock fn open_tab(&self, tab: impl AppTab) { - self.lock_mut().new_tab = Some(tab.make_tab(self.clone())); + self.0.new_tab.set(Some(tab.make_tab(self.clone()))); } - /// mut lock - fn new_tab(&self) -> Option> { - self.lock_mut().new_tab.take() + fn take_tab_to_open(&self) -> Option> { + self.0.new_tab.take() } fn selected(&self) -> ItemSelection { - self.lock().selected + self.0.selected.get() } - /// mut lock fn set_selected(&self, s: ItemSelection) { - self.lock_mut().selected = s; + self.0.selected.set(s); } } struct AppCtx { file: PathBuf, code: Bytecode, - selected: ItemSelection, + selected: Cell, /// To open a tab from another tab. /// This can't be done directly because this would need a mutable reference to a tree and the tree owns the tab. - new_tab: Option>, + new_tab: Cell>>, } impl AppCtx { @@ -241,16 +230,16 @@ impl AppCtx { AppCtx { file, code, - selected: ItemSelection::None, - new_tab: None, + selected: Cell::new(ItemSelection::None), + new_tab: Cell::new(None), } } } fn default_tabs_ui(ctx: AppCtxHandle) -> DynamicTree { let mut tree = Tree::new(vec![ - InfoView::default().make_tab(ctx.clone()), SyncInspectorView::default().make_tab(ctx.clone()), + InfoView::default().make_tab(ctx.clone()), ]); tree.split_left( diff --git a/hlbc-gui/src/views/inspector.rs b/hlbc-gui/src/views/inspector.rs index 802a25e..a467dee 100644 --- a/hlbc-gui/src/views/inspector.rs +++ b/hlbc-gui/src/views/inspector.rs @@ -1,7 +1,9 @@ use std::ops::Deref; use eframe::egui::style::Margin; -use eframe::egui::{Color32, Frame, Grid, RichText, ScrollArea, TextStyle, Ui, WidgetText}; +use eframe::egui::{ + Color32, Frame, Grid, Hyperlink, Link, RichText, ScrollArea, TextStyle, Ui, WidgetText, +}; use hlbc::types::{FunPtr, RefField, RefFun, RefGlobal, RefString, RefType}; use hlbc::Bytecode; @@ -68,31 +70,42 @@ fn inspector_ui(ui: &mut Ui, ctx: AppCtxHandle, item: ItemSelection) { ScrollArea::vertical() .id_source("functions_scroll_area") .auto_shrink([false, false]) - .show(ui, |ui| { - let code = ctx.code(); - let code = code.deref(); - match item { - ItemSelection::Fun(fun) => { - function_inspector(ui, fun, code); - } - ItemSelection::Class(t) => { - class_inspector(ui, t, code); - } - ItemSelection::Global(g) => { - global_inspector(ui, g, code); - } - ItemSelection::String(s) => { - string_inspector(ui, s, code); - } - _ => { - ui.label("Select a function or a class."); - } + .show(ui, |ui| match item { + ItemSelection::Fun(fun) => { + function_inspector(ui, ctx, fun); + } + ItemSelection::Class(t) => { + class_inspector(ui, ctx, t); + } + ItemSelection::Global(g) => { + global_inspector(ui, ctx, g); + } + ItemSelection::String(s) => { + string_inspector(ui, ctx, s); + } + _ => { + ui.label("Select a function or a class."); } }); }); } -fn function_inspector(ui: &mut Ui, fun: RefFun, code: &Bytecode) { +fn inspector_link(ui: &mut Ui, ctx: AppCtxHandle, item: ItemSelection) { + let res = ui + .add(Link::new(item.name(ctx.code().deref()))) + .context_menu(|ui| { + if ui.button("Open in inspector").clicked() { + ctx.open_tab(InspectorView::new(item, ctx.code().deref())); + ui.close_menu(); + } + }); + if res.clicked() { + ctx.set_selected(item); + } +} + +fn function_inspector(ui: &mut Ui, ctx: AppCtxHandle, fun: RefFun) { + let code = ctx.code(); match fun.resolve(code) { FunPtr::Fun(f) => { ui.heading(format!( @@ -101,10 +114,10 @@ fn function_inspector(ui: &mut Ui, fun: RefFun, code: &Bytecode) { f.findex.0 )); if let Some(parent) = f.parent { - ui.label(format!( - "static/instance method of {}", - parent.display_id(code) - )); + ui.horizontal(|ui| { + ui.label("static/instance method of"); + inspector_link(ui, ctx.clone(), ItemSelection::Class(parent)); + }); } else { ui.label("Probably a closure."); } @@ -154,14 +167,25 @@ fn function_inspector(ui: &mut Ui, fun: RefFun, code: &Bytecode) { } } -fn class_inspector(ui: &mut Ui, t: RefType, code: &Bytecode) { +fn class_inspector(ui: &mut Ui, ctx: AppCtxHandle, t: RefType) { + let code = ctx.code(); ui.heading(format!("Class : {}", t.display_id(code))); if let Some(obj) = t.resolve_as_obj(&code.types) { if let Some(super_) = obj.super_ { - ui.label(format!("extends {}", super_.display_id(code))); + ui.horizontal(|ui| { + ui.label("extends"); + inspector_link(ui, ctx.clone(), ItemSelection::Class(super_)); + }); } if obj.global.0 >= 1 { - ui.label(format!("initialized by global {}", obj.global.0 - 1)); + ui.horizontal(|ui| { + ui.label("initialized by global"); + inspector_link( + ui, + ctx.clone(), + ItemSelection::Global(RefGlobal(obj.global.0 - 1)), + ); + }); } if obj.own_fields.is_empty() { @@ -176,11 +200,12 @@ fn class_inspector(ui: &mut Ui, t: RefType, code: &Bytecode) { for (i, f) in obj.own_fields.iter().enumerate() { ui.label(f.name.resolve(&code.strings)); ui.label(f.t.display_id(code)); - if let Some(binding) = obj + if let Some(&binding) = obj .bindings .get(&RefField(i + obj.fields.len() - obj.own_fields.len())) { - ui.monospace(format!("bound to {}", binding.display_id(code))); + ui.monospace("bound to"); + inspector_link(ui, ctx.clone(), ItemSelection::Fun(binding)); } else { ui.monospace("variable"); } @@ -201,7 +226,7 @@ fn class_inspector(ui: &mut Ui, t: RefType, code: &Bytecode) { .show(ui, |ui| { for f in &obj.protos { ui.label(f.name.resolve(&code.strings)); - ui.label(f.findex.display_id(code).to_string()); + inspector_link(ui, ctx.clone(), ItemSelection::Fun(f.findex)); ui.end_row(); } }); @@ -212,11 +237,17 @@ fn class_inspector(ui: &mut Ui, t: RefType, code: &Bytecode) { } } -fn global_inspector(ui: &mut Ui, g: RefGlobal, code: &Bytecode) { +fn global_inspector(ui: &mut Ui, ctx: AppCtxHandle, g: RefGlobal) { ui.heading(format!("Global@{}", g.0)); - ui.label(format!("Type : {}", code.globals[g.0].display_id(code))); + ui.label(format!( + "Type : {}", + ctx.code().globals[g.0].display_id(ctx.code().deref()) + )); - if let (Some(&cst), Some(constants)) = (code.globals_initializers.get(&g), &code.constants) { + if let (Some(&cst), Some(constants)) = ( + ctx.code().globals_initializers.get(&g), + &ctx.code().constants, + ) { let def = &constants[cst]; ui.label(format!("{:?}", def.fields)); } else { @@ -224,9 +255,9 @@ fn global_inspector(ui: &mut Ui, g: RefGlobal, code: &Bytecode) { } } -fn string_inspector(ui: &mut Ui, s: RefString, code: &Bytecode) { +fn string_inspector(ui: &mut Ui, ctx: AppCtxHandle, s: RefString) { ui.heading(format!("String@{}", s.0)); ui.separator(); ui.add_space(4.0); - ui.label(RichText::new(s.resolve(&code.strings)).monospace()); + ui.label(RichText::new(s.resolve(&ctx.code().strings)).monospace()); }