Skip to content

Commit 477f5ec

Browse files
committed
Fix the context actions!
1 parent b5c226a commit 477f5ec

File tree

6 files changed

+32
-42
lines changed

6 files changed

+32
-42
lines changed

libbs/api/decompiler_interface.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,13 @@ def find_current_decompiler(force: str = None) -> Optional[str]:
991991
if "License is not valid" in str(e):
992992
_l.warning("Binary Ninja license is invalid, skipping...")
993993

994+
# Ghidra
995+
this_obj = DecompilerInterface._find_global_in_call_frames("__this__")
996+
if (this_obj is not None) and (hasattr(this_obj, "currentProgram")):
997+
available.add(GHIDRA_DECOMPILER)
998+
if not force:
999+
return GHIDRA_DECOMPILER
1000+
9941001
# angr-management
9951002
try:
9961003
import angr
@@ -1015,15 +1022,6 @@ def find_current_decompiler(force: str = None) -> Optional[str]:
10151022
except Exception:
10161023
pass
10171024

1018-
# Ghidra
1019-
# It is always available, and we don't have an import check, because when started in headless mode we create
1020-
# the interface by which ghidra can now be imported.
1021-
this_obj = DecompilerInterface._find_global_in_call_frames("__this__")
1022-
if (this_obj is not None) and (hasattr(this_obj, "currentProgram")):
1023-
available.add(GHIDRA_DECOMPILER)
1024-
if not force:
1025-
return GHIDRA_DECOMPILER
1026-
10271025
if not available:
10281026
_l.critical("LibBS was unable to find the current decompiler you are running in or any headless instances!")
10291027
return None

libbs/decompilers/ghidra/README.md

Lines changed: 0 additions & 8 deletions
This file was deleted.

libbs/decompilers/ghidra/compat/bridge.py

Lines changed: 0 additions & 2 deletions
This file was deleted.

libbs/decompilers/ghidra/compat/imports.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def get_private_class(path: str):
2222
from ghidra.program.model.listing import CodeUnit
2323
from ghidra.app.cmd.comments import SetCommentCmd
2424
from ghidra.app.cmd.label import RenameLabelCmd
25-
from ghidra.app.context import ProgramLocationContextAction
25+
from ghidra.app.context import ProgramLocationContextAction, ProgramLocationActionContext
2626
from ghidra.app.decompiler import DecompInterface
2727
from ghidra.app.plugin.core.analysis import AutoAnalysisManager
2828
from ghidra.app.util.cparser.C import CParserUtils
@@ -31,6 +31,7 @@ def get_private_class(path: str):
3131
from ghidra.util.data import DataTypeParser
3232
from ghidra.util.exception import CancelledException
3333
from docking.action import MenuData
34+
from docking.action.builder import ActionBuilder
3435

3536
EnumDB = get_private_class("ghidra.program.database.data.EnumDB")
3637
StructureDB = get_private_class("ghidra.program.database.data.StructureDB")
@@ -48,7 +49,9 @@ def get_private_class(path: str):
4849
"CodeSymbol",
4950
"FunctionSymbol",
5051
"ProgramLocationContextAction",
52+
"ProgramLocationActionContext",
5153
"MenuData",
54+
"ActionBuilder",
5255
"HighFunctionDBUtil",
5356
"DataTypeConflictHandler",
5457
"StructureDataType",

libbs/decompilers/ghidra/hooks.py

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -207,21 +207,20 @@ def create_data_monitor(deci: "GhidraDecompilerInterface"):
207207
return data_monitor
208208

209209

210-
def create_context_action(name, action_string, callback_func, category=None):
211-
"""
212-
TODO: this is broken due to JPype: you can't subclass a Ghidra class. To fix this requires
213-
creating a Java class that allows us to _implement_ ProgramLocationContextAction in Python.
214-
"""
215-
from .compat.imports import ProgramLocationContextAction, MenuData
216-
217-
# XXX: you can't ever use super().__init__() due to JPype limitations with Java class subclassing
218-
class GenericDecompilerCtxAction(ProgramLocationContextAction):
219-
def actionPerformed(self, ctx):
220-
threading.Thread(target=callback_func, daemon=True).start()
221-
222-
action = GenericDecompilerCtxAction(name, category)
223-
category_list = category.split("/") if category else []
224-
category_start = category_list[0] if category_list else category
225-
action.setPopupMenuData(MenuData(category_list + [action_string], None, category_start))
226-
227-
return action
210+
def create_context_action(name, action_string, callback_func, category=None, plugin_name="libbs_ghidra", tool=None):
211+
from .compat.imports import ProgramLocationActionContext, ActionBuilder
212+
def _invoke(ctx: ProgramLocationActionContext):
213+
threading.Thread(target=callback_func, daemon=True).start()
214+
215+
menu_path = []
216+
if category is not None and "/" in category:
217+
menu_path.extend(category.split("/"))
218+
menu_path.append(action_string)
219+
220+
b = (ActionBuilder(name, plugin_name)
221+
.popupMenuPath(list(menu_path))
222+
.withContext(ProgramLocationActionContext)
223+
.validContextWhen(lambda ctx: ctx is not None and ctx.getAddress() is not None)
224+
.onAction(_invoke))
225+
226+
return b.buildAndInstall(tool)

libbs/decompilers/ghidra/interface.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,6 @@ def gui_run_on_main_thread(self, func, *args, **kwargs):
153153
return self._results_queue.get()
154154

155155
def gui_register_ctx_menu(self, name, action_string, callback_func, category=None) -> bool:
156-
# TODO: this is currently broken, see create_context_action for why
157-
return None
158156
from .hooks import create_context_action
159157

160158
def callback_func_wrap(*args, **kwargs):
@@ -163,8 +161,10 @@ def callback_func_wrap(*args, **kwargs):
163161
except Exception as e:
164162
self.warning(f"Exception in ctx menu callback {name}: {e}")
165163
raise
166-
ctx_menu_action = create_context_action(name, action_string, callback_func_wrap, category or "LibBS")
167-
self.flat_api.getState().getTool().addAction(ctx_menu_action)
164+
create_context_action(
165+
name, action_string, callback_func_wrap, category=(category or "LibBS"),
166+
tool=self.flat_api.getState().getTool()
167+
)
168168
return True
169169

170170
def gui_ask_for_string(self, question, title="Plugin Question") -> str:

0 commit comments

Comments
 (0)