diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiMethods.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiMethods.java index 46771fdb6a..fcf597c7e8 100644 --- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiMethods.java +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiMethods.java @@ -58,10 +58,7 @@ public class TraceJdiMethods implements RmiMethods { // // } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh VM", - schema = "VirtualMachine") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh VM", schema = "VirtualMachine") public void refresh_vm(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshVM")) { String path = obj.getPath(); @@ -70,10 +67,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh process", - schema = "ProcessRef") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh process", schema = "ProcessRef") public void refresh_process(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshProcess")) { String path = obj.getPath(); @@ -82,10 +76,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh thread groups", - schema = "ThreadGroupReferenceContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh thread groups", schema = "ThreadGroupReferenceContainer") public void refresh_thread_groups(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshThreadGroups")) { String path = obj.getPath(); @@ -100,10 +91,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh thread group", - schema = "ThreadGroupReferenceProxy") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh thread group", schema = "ThreadGroupReferenceProxy") public void refresh_thread_group_proxy(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshThreadGroup")) { String path = obj.getPath(); @@ -118,10 +106,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh thread group", - schema = "ThreadGroupReference") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh thread group", schema = "ThreadGroupReference") public void refresh_thread_group(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshThreadGroup")) { String path = obj.getPath(); @@ -130,10 +115,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh threads", - schema = "ThreadContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh threads", schema = "ThreadContainer") public void refresh_threads(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshThreads")) { String path = obj.getPath(); @@ -143,10 +125,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh threads", - schema = "ThreadReferenceContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh threads", schema = "ThreadReferenceContainer") public void refresh_threadrefs(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshThreads")) { String path = obj.getPath(); @@ -166,10 +145,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh Thread", - schema = "Thread") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh Thread", schema = "Thread") public void refresh_thread(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshThread")) { String path = obj.getPath(); @@ -185,30 +161,21 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh registers", - schema = "RegisterContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh registers", schema = "RegisterContainer") public void refresh_registers(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshRegisters")) { cmds.ghidraTracePutFrames(); } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh modules", - schema = "ModuleReferenceContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh modules", schema = "ModuleReferenceContainer") public void refresh_modules(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshModules")) { cmds.putModuleReferenceContainer(); } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh module", - schema = "ModuleReference") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh module", schema = "ModuleReference") public void refresh_module(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshModule")) { String path = obj.getPath(); @@ -217,10 +184,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh monitor info", - schema = "MonitorInfoContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh monitor info", schema = "MonitorInfoContainer") public void refresh_monitors(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshMonitorInfo")) { String path = obj.getPath(); @@ -233,10 +197,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh monitor info", - schema = "MonitorInfo") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh monitor info", schema = "MonitorInfo") public void refresh_monitor_info(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshMonitorInfo")) { String path = obj.getPath(); @@ -245,10 +206,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh fields", - schema = "FieldContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh fields", schema = "FieldContainer") public void refresh_fields(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshFields")) { String path = obj.getPath(); @@ -272,10 +230,7 @@ public class TraceJdiMethods implements RmiMethods { // } // } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh objects", - schema = "ObjectReferenceContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh objects", schema = "ObjectReferenceContainer") public void refresh_objects(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshObjects")) { String path = obj.getPath(); @@ -300,10 +255,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh object", - schema = "ObjectReferenceProxy") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh object", schema = "ObjectReferenceProxy") public void refresh_object_proxy(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshObject")) { String path = obj.getPath(); @@ -324,10 +276,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh object", - schema = "ObjectReference") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh object", schema = "ObjectReference") public void refresh_object(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshInstance")) { String path = obj.getPath(); @@ -336,10 +285,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh methods", - schema = "MethodContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh methods", schema = "MethodContainer") public void refresh_methods(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshMethods")) { String path = obj.getPath(); @@ -349,10 +295,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh method", - schema = "Method") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh method", schema = "Method") public void refresh_method(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshMethod")) { String path = obj.getPath(); @@ -361,10 +304,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh arguments", - schema = "ArgumentContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh arguments", schema = "ArgumentContainer") public void refresh_arguments(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshArguments")) { String path = obj.getPath(); @@ -374,15 +314,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "load_class", - display = "Load class", - schema = "ReferenceTypeContainer") + @RmiMethodRegistry.TraceMethod(action = "load_class", display = "Load class", schema = "ReferenceTypeContainer") public void find_class(RmiTraceObject obj, - @TargetMethod.Param( - description = "Class to open", - display = "Class", - name = "find") String targetClass) { + @TargetMethod.Param(description = "Class to open", display = "Class", name = "find") String targetClass) { try (RmiTransaction tx = cmds.state.trace.openTx("FindClass")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -393,18 +327,12 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh_memory", - display = "Refresh memory", - schema = "Memory") + @RmiMethodRegistry.TraceMethod(action = "refresh_memory", display = "Refresh memory", schema = "Memory") public void refresh_memory(RmiTraceObject obj) { refresh_reference_types(obj); } - @RmiMethodRegistry.TraceMethod( - action = "refresh_types", - display = "Refresh reference types", - schema = "ReferenceTypeContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh_types", display = "Refresh reference types", schema = "ReferenceTypeContainer") public void refresh_reference_types(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshReferenceTypes")) { String path = obj.getPath(); @@ -446,10 +374,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh reference type", - schema = "ReferenceTypeProxy") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh reference type", schema = "ReferenceTypeProxy") public void refresh_reference_type_proxy(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshReferenceType")) { String path = obj.getPath(); @@ -470,10 +395,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh reference type", - schema = "ReferenceType") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh reference type", schema = "ReferenceType") public void refresh_reference_type(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshReferenceType")) { String path = obj.getPath(); @@ -482,10 +404,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "load", - display = "Load reference", - schema = "ReferenceType") + @RmiMethodRegistry.TraceMethod(action = "load", display = "Load reference", schema = "ReferenceType") public void load_reftype(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshReferenceType")) { VirtualMachine vm = manager.getJdi().getCurrentVM(); @@ -496,10 +415,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh variables", - schema = "VariableContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh variables", schema = "VariableContainer") public void refresh_variables(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshVariables")) { String path = obj.getPath(); @@ -525,10 +441,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh variable", - schema = "Variable") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh variable", schema = "Variable") public void refresh_variable(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshVariable")) { String path = obj.getPath(); @@ -539,10 +452,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh locations", - schema = "LocationContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh locations", schema = "LocationContainer") public void refresh_locations(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshLocations")) { String path = obj.getPath(); @@ -559,10 +469,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh location", - schema = "Location") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh location", schema = "Location") public void refresh_location(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshLocation")) { String path = obj.getPath(); @@ -571,30 +478,21 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh breakpoints", - schema = "BreakpointContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh breakpoints", schema = "BreakpointContainer") public void refresh_breakpoints(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshBreakpoints")) { cmds.putBreakpoints(); } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh events", - schema = "EventContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh events", schema = "EventContainer") public void refresh_events(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshEvents")) { cmds.putEvents(); } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh values", - schema = "ValueContainer") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh values", schema = "ValueContainer") public void refresh_values(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshValues")) { String path = obj.getPath(); @@ -606,10 +504,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh value", - schema = "Value") + @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh value", schema = "Value") public void refresh_value(RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshLocation")) { String path = obj.getPath(); @@ -618,10 +513,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "set", - display = "Set value", - schema = "Variable") + @RmiMethodRegistry.TraceMethod(action = "set", display = "Set value", schema = "Variable") public void set_value_lvar(RmiTraceObject obj, String value) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshLocation")) { String path = obj.getPath(); @@ -630,10 +522,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "set", - display = "Set value", - schema = "Field") + @RmiMethodRegistry.TraceMethod(action = "set", display = "Set value", schema = "Field") public void set_value_field(RmiTraceObject obj, String value) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshLocation")) { String path = obj.getPath(); @@ -674,10 +563,7 @@ public class TraceJdiMethods implements RmiMethods { manager.getHooks().setState(thread.virtualMachine()); } - @RmiMethodRegistry.TraceMethod( - action = "suspend", - display = "Suspend", - schema = "VirtualMachine") + @RmiMethodRegistry.TraceMethod(action = "suspend", display = "Suspend", schema = "VirtualMachine") public void suspend(RmiTraceObject obj) { Object object = getObjectFromPath(obj.getPath()); if (object instanceof ThreadReference thread) { @@ -691,28 +577,21 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "interrupt", - display = "Interrupt", - schema = "VirtualMachine") + @RmiMethodRegistry.TraceMethod(action = "interrupt", display = "Interrupt", schema = "VirtualMachine") public void interrupt(RmiTraceObject obj) { suspend(obj); } // NB: For the VirtualMachine, the step methods add requests for break-on-step for all threads. // These requests will remain pending until the VM is resumed. - @RmiMethodRegistry.TraceMethod( - action = "step_into", - display = "Step into", - schema = "VirtualMachine") + @RmiMethodRegistry.TraceMethod(action = "step_into", display = "Step into", schema = "VirtualMachine") public void step_vm_into(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); List threads = getThreadsFromValue(obj); for (ThreadReference thread : threads) { try { StepRequest stepReq = vm.eventRequestManager() - .createStepRequest(thread, StepRequest.STEP_MIN, - StepRequest.STEP_INTO); + .createStepRequest(thread, StepRequest.STEP_MIN, StepRequest.STEP_INTO); stepReq.enable(); } catch (DuplicateRequestException dre) { @@ -722,18 +601,14 @@ public class TraceJdiMethods implements RmiMethods { vm.resume(); } - @RmiMethodRegistry.TraceMethod( - action = "step_over", - display = "Step over", - schema = "VirtualMachine") + @RmiMethodRegistry.TraceMethod(action = "step_over", display = "Step over", schema = "VirtualMachine") public void step_vm_over(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); List threads = getThreadsFromValue(obj); for (ThreadReference thread : threads) { try { StepRequest stepReq = vm.eventRequestManager() - .createStepRequest(thread, StepRequest.STEP_MIN, - StepRequest.STEP_OVER); + .createStepRequest(thread, StepRequest.STEP_MIN, StepRequest.STEP_OVER); stepReq.enable(); } catch (DuplicateRequestException dre) { @@ -743,18 +618,14 @@ public class TraceJdiMethods implements RmiMethods { vm.resume(); } - @RmiMethodRegistry.TraceMethod( - action = "step_out", - display = "Step out", - schema = "VirtualMachine") + @RmiMethodRegistry.TraceMethod(action = "step_out", display = "Step out", schema = "VirtualMachine") public void step_vm_out(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); List threads = getThreadsFromValue(obj); for (ThreadReference thread : threads) { try { StepRequest stepReq = vm.eventRequestManager() - .createStepRequest(thread, StepRequest.STEP_MIN, - StepRequest.STEP_OUT); + .createStepRequest(thread, StepRequest.STEP_MIN, StepRequest.STEP_OUT); stepReq.enable(); } catch (DuplicateRequestException dre) { @@ -769,8 +640,7 @@ public class TraceJdiMethods implements RmiMethods { VirtualMachine vm = manager.getJdi().getCurrentVM(); ThreadReference thread = (ThreadReference) getObjectFromPath(obj.getPath()); StepRequest stepReq = vm.eventRequestManager() - .createStepRequest(thread, StepRequest.STEP_MIN, - StepRequest.STEP_INTO); + .createStepRequest(thread, StepRequest.STEP_MIN, StepRequest.STEP_INTO); stepReq.enable(); vm.resume(); } @@ -780,8 +650,7 @@ public class TraceJdiMethods implements RmiMethods { VirtualMachine vm = manager.getJdi().getCurrentVM(); ThreadReference thread = (ThreadReference) getObjectFromPath(obj.getPath()); StepRequest stepReq = vm.eventRequestManager() - .createStepRequest(thread, StepRequest.STEP_OVER, - StepRequest.STEP_INTO); + .createStepRequest(thread, StepRequest.STEP_OVER, StepRequest.STEP_INTO); stepReq.enable(); vm.resume(); } @@ -791,8 +660,7 @@ public class TraceJdiMethods implements RmiMethods { VirtualMachine vm = manager.getJdi().getCurrentVM(); ThreadReference thread = (ThreadReference) getObjectFromPath(obj.getPath()); StepRequest stepReq = vm.eventRequestManager() - .createStepRequest(thread, StepRequest.STEP_OUT, - StepRequest.STEP_INTO); + .createStepRequest(thread, StepRequest.STEP_OUT, StepRequest.STEP_INTO); stepReq.enable(); vm.resume(); } @@ -800,10 +668,7 @@ public class TraceJdiMethods implements RmiMethods { // public void step_advance(Object obj) {} // public void step_return(Object obj) {} - @RmiMethodRegistry.TraceMethod( - action = "thread_interrupt", - display = "Thread Interrupt", - schema = "Thread") + @RmiMethodRegistry.TraceMethod(action = "thread_interrupt", display = "Thread Interrupt", schema = "Thread") public void thread_interrupt(RmiTraceObject obj) { Object object = getObjectFromPath(obj.getPath()); if (object instanceof ThreadReference thread) { @@ -812,10 +677,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "pop_stack", - display = "Pop stack", - schema = "StackFrame") + @RmiMethodRegistry.TraceMethod(action = "pop_stack", display = "Pop stack", schema = "StackFrame") public void pop_stack(RmiTraceObject obj) { StackFrame frame = (StackFrame) getObjectFromPath(obj.getPath()); ThreadReference thread = frame.thread(); @@ -823,68 +685,49 @@ public class TraceJdiMethods implements RmiMethods { thread.popFrames(frame); } catch (IncompatibleThreadStateException e) { - Msg.out("Incompatible thread state for pop"); + Msg.error(this, "Incompatible thread state for pop"); } } - @RmiMethodRegistry.TraceMethod( - action = "break_location", - display = "Break on execute", - schema = "Location") + @RmiMethodRegistry.TraceMethod(action = "break_location", display = "Break on execute", schema = "Location") public void break_location(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof Location loc) { - BreakpointRequest brkReq = vm.eventRequestManager() - .createBreakpointRequest(loc); + BreakpointRequest brkReq = vm.eventRequestManager().createBreakpointRequest(loc); brkReq.enable(); cmds.putBreakpoints(); } } - @RmiMethodRegistry.TraceMethod( - action = "break_field_access", - display = "Break on access", - schema = "Field") + @RmiMethodRegistry.TraceMethod(action = "break_field_access", display = "Break on access", schema = "Field") public void break_access(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof Field field) { - AccessWatchpointRequest brkReq = vm.eventRequestManager() - .createAccessWatchpointRequest(field); + AccessWatchpointRequest brkReq = + vm.eventRequestManager().createAccessWatchpointRequest(field); brkReq.enable(); cmds.putBreakpoints(); } } - @RmiMethodRegistry.TraceMethod( - action = "break_field_modified", - display = "Break on modify", - schema = "Field") + @RmiMethodRegistry.TraceMethod(action = "break_field_modified", display = "Break on modify", schema = "Field") public void break_modify(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof Field field) { - ModificationWatchpointRequest brkReq = vm.eventRequestManager() - .createModificationWatchpointRequest(field); + ModificationWatchpointRequest brkReq = + vm.eventRequestManager().createModificationWatchpointRequest(field); brkReq.enable(); cmds.putBreakpoints(); } } - @RmiMethodRegistry.TraceMethod( - action = "break_exception", - display = "Break on exception", - schema = "ReferenceType") + @RmiMethodRegistry.TraceMethod(action = "break_exception", display = "Break on exception", schema = "ReferenceType") public void break_exception(RmiTraceObject obj, - @TargetMethod.Param( - description = "Caught exceptions will be notified", - display = "NotifyCaught", - name = "notifyC") Boolean notifyCaught, - @TargetMethod.Param( - description = "Uncaught exceptions will be notified", - display = "NotifyUncaught", - name = "notifyU") Boolean notifyUncaught) { + @TargetMethod.Param(description = "Caught exceptions will be notified", display = "NotifyCaught", name = "notifyC") Boolean notifyCaught, + @TargetMethod.Param(description = "Uncaught exceptions will be notified", display = "NotifyUncaught", name = "notifyU") Boolean notifyUncaught) { VirtualMachine vm = manager.getJdi().getCurrentVM(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ReferenceType reftype) { @@ -897,8 +740,7 @@ public class TraceJdiMethods implements RmiMethods { private void break_started(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); - ThreadStartRequest brkReq = vm.eventRequestManager() - .createThreadStartRequest(); + ThreadStartRequest brkReq = vm.eventRequestManager().createThreadStartRequest(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ThreadReference ref) { brkReq.putProperty("Thread", ref); @@ -908,26 +750,19 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_started", - display = "Break on thread start", - schema = "EventContainer") + @RmiMethodRegistry.TraceMethod(action = "break_started", display = "Break on thread start", schema = "EventContainer") public void break_started_container(RmiTraceObject obj) { break_started(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_started", - display = "Break on thread start", - schema = "Thread") + @RmiMethodRegistry.TraceMethod(action = "break_started", display = "Break on thread start", schema = "Thread") public void break_started_thread(RmiTraceObject obj) { break_started(obj); } private void break_death(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); - ThreadDeathRequest brkReq = vm.eventRequestManager() - .createThreadDeathRequest(); + ThreadDeathRequest brkReq = vm.eventRequestManager().createThreadDeathRequest(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ThreadReference ref) { brkReq.putProperty("Thread", ref); @@ -937,38 +772,27 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_death", - display = "Break on thread exit", - schema = "EventContainer") + @RmiMethodRegistry.TraceMethod(action = "break_death", display = "Break on thread exit", schema = "EventContainer") public void break_death_container(RmiTraceObject obj) { break_death(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_death", - display = "Break on thread exit", - schema = "Thread") + @RmiMethodRegistry.TraceMethod(action = "break_death", display = "Break on thread exit", schema = "Thread") public void break_death_thread(RmiTraceObject obj) { break_death(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_vm_death", - display = "Break on VM death", - schema = "VirtualMachine") + @RmiMethodRegistry.TraceMethod(action = "break_vm_death", display = "Break on VM death", schema = "VirtualMachine") public void break_vm_death(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); - VMDeathRequest brkReq = vm.eventRequestManager() - .createVMDeathRequest(); + VMDeathRequest brkReq = vm.eventRequestManager().createVMDeathRequest(); brkReq.enable(); cmds.putEvents(); } private void break_enter(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); - MethodEntryRequest brkReq = vm.eventRequestManager() - .createMethodEntryRequest(); + MethodEntryRequest brkReq = vm.eventRequestManager().createMethodEntryRequest(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ReferenceType reftype) { brkReq.putProperty("Class", reftype); @@ -986,42 +810,29 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_enter", - display = "Break on method enter", - schema = "EventContainer") + @RmiMethodRegistry.TraceMethod(action = "break_enter", display = "Break on method enter", schema = "EventContainer") public void break_enter_container(RmiTraceObject obj) { break_enter(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_enter", - display = "Break on method enter", - schema = "ReferenceType") + @RmiMethodRegistry.TraceMethod(action = "break_enter", display = "Break on method enter", schema = "ReferenceType") public void break_enter_reftype(RmiTraceObject obj) { break_enter(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_enter_instance", - display = "Break on method enter", - schema = "ObjectReference") + @RmiMethodRegistry.TraceMethod(action = "break_enter_instance", display = "Break on method enter", schema = "ObjectReference") public void break_enter_instance(RmiTraceObject obj) { break_enter(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_enter_thread", - display = "Break on method enter", - schema = "Thread") + @RmiMethodRegistry.TraceMethod(action = "break_enter_thread", display = "Break on method enter", schema = "Thread") public void break_enter_thread(RmiTraceObject obj) { break_enter(obj); } private void break_exit(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); - MethodExitRequest brkReq = vm.eventRequestManager() - .createMethodExitRequest(); + MethodExitRequest brkReq = vm.eventRequestManager().createMethodExitRequest(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ReferenceType reftype) { brkReq.putProperty("Class", reftype); @@ -1039,42 +850,29 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_exit", - display = "Break on method exit", - schema = "EventContainer") + @RmiMethodRegistry.TraceMethod(action = "break_exit", display = "Break on method exit", schema = "EventContainer") public void break_exit_container(RmiTraceObject obj) { break_exit(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_exit", - display = "Break on method exit", - schema = "ReferenceType") + @RmiMethodRegistry.TraceMethod(action = "break_exit", display = "Break on method exit", schema = "ReferenceType") public void break_exit_reftype(RmiTraceObject obj) { break_exit(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_exit", - display = "Break on method exit", - schema = "ObjectReference") + @RmiMethodRegistry.TraceMethod(action = "break_exit", display = "Break on method exit", schema = "ObjectReference") public void break_exit_instance(RmiTraceObject obj) { break_exit(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_exit", - display = "Break on method exit", - schema = "Thread") + @RmiMethodRegistry.TraceMethod(action = "break_exit", display = "Break on method exit", schema = "Thread") public void break_exit_thread(RmiTraceObject obj) { break_exit(obj); } private void break_load(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); - ClassPrepareRequest brkReq = vm.eventRequestManager() - .createClassPrepareRequest(); + ClassPrepareRequest brkReq = vm.eventRequestManager().createClassPrepareRequest(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ReferenceType reftype) { brkReq.putProperty("Class", reftype); @@ -1084,42 +882,32 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_load", - display = "Break on class load", - schema = "EventContainer") + @RmiMethodRegistry.TraceMethod(action = "break_load", display = "Break on class load", schema = "EventContainer") public void break_load_container(RmiTraceObject obj) { break_load(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_load", - display = "Break on class load", - schema = "ReferenceType") + @RmiMethodRegistry.TraceMethod(action = "break_load", display = "Break on class load", schema = "ReferenceType") public void break_load_reftype(RmiTraceObject obj) { break_load(obj); } private void break_unload(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); - ClassUnloadRequest brkReq = vm.eventRequestManager() - .createClassUnloadRequest(); + ClassUnloadRequest brkReq = vm.eventRequestManager().createClassUnloadRequest(); brkReq.enable(); cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_unload", - display = "Break on class unload", - schema = "EventContainer") + @RmiMethodRegistry.TraceMethod(action = "break_unload", display = "Break on class unload", schema = "EventContainer") public void break_unload_container(RmiTraceObject obj) { break_unload(obj); } private void break_mon_enter_contention(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); - MonitorContendedEnterRequest brkReq = vm.eventRequestManager() - .createMonitorContendedEnterRequest(); + MonitorContendedEnterRequest brkReq = + vm.eventRequestManager().createMonitorContendedEnterRequest(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ReferenceType reftype) { brkReq.putProperty("Class", reftype); @@ -1137,42 +925,30 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_enter_contention", - display = "Break on monitor contended enter", - schema = "EventContainer") + @RmiMethodRegistry.TraceMethod(action = "break_mon_enter_contention", display = "Break on monitor contended enter", schema = "EventContainer") public void break_mon_enter_contention_container(RmiTraceObject obj) { break_mon_enter_contention(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_enter_contention", - display = "Break on monitor contended enter", - schema = "ReferenceType") + @RmiMethodRegistry.TraceMethod(action = "break_mon_enter_contention", display = "Break on monitor contended enter", schema = "ReferenceType") public void break_mon_enter_contention_reftype(RmiTraceObject obj) { break_mon_enter_contention(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_enter_contention", - display = "Break on monitor contended enter", - schema = "ObjectReference") + @RmiMethodRegistry.TraceMethod(action = "break_mon_enter_contention", display = "Break on monitor contended enter", schema = "ObjectReference") public void break_mon_enter_contention_instance(RmiTraceObject obj) { break_mon_enter_contention(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_enter_contention", - display = "Break on monitor contended enter", - schema = "Thread") + @RmiMethodRegistry.TraceMethod(action = "break_mon_enter_contention", display = "Break on monitor contended enter", schema = "Thread") public void break_mon_enter_contention_thread(RmiTraceObject obj) { break_mon_enter_contention(obj); } private void break_mon_entered_contention(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); - MonitorContendedEnteredRequest brkReq = vm.eventRequestManager() - .createMonitorContendedEnteredRequest(); + MonitorContendedEnteredRequest brkReq = + vm.eventRequestManager().createMonitorContendedEnteredRequest(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ReferenceType reftype) { brkReq.putProperty("Class", reftype); @@ -1190,42 +966,29 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_entered_contention", - display = "Break on monitor contented entered", - schema = "EventContainer") + @RmiMethodRegistry.TraceMethod(action = "break_mon_entered_contention", display = "Break on monitor contented entered", schema = "EventContainer") public void break_mon_entered_contention_container(RmiTraceObject obj) { break_mon_entered_contention(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_entered_contention", - display = "Break on monitor contented entered", - schema = "ReferenceType") + @RmiMethodRegistry.TraceMethod(action = "break_mon_entered_contention", display = "Break on monitor contented entered", schema = "ReferenceType") public void break_mon_entered_contention_reftype(RmiTraceObject obj) { break_mon_entered_contention(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_entered_contention", - display = "Break on monitor contented entered", - schema = "ObjectReference") + @RmiMethodRegistry.TraceMethod(action = "break_mon_entered_contention", display = "Break on monitor contented entered", schema = "ObjectReference") public void break_mon_entered_contention_instance(RmiTraceObject obj) { break_mon_entered_contention(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_entered_contention", - display = "Break on monitor contented entered", - schema = "Thread") + @RmiMethodRegistry.TraceMethod(action = "break_mon_entered_contention", display = "Break on monitor contented entered", schema = "Thread") public void break_mon_entered_contention_thread(RmiTraceObject obj) { break_mon_entered_contention(obj); } private void break_mon_wait(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); - MonitorWaitRequest brkReq = vm.eventRequestManager() - .createMonitorWaitRequest(); + MonitorWaitRequest brkReq = vm.eventRequestManager().createMonitorWaitRequest(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ReferenceType reftype) { brkReq.putProperty("Class", reftype); @@ -1243,42 +1006,29 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_wait", - display = "Break on monitor wait", - schema = "EventContainer") + @RmiMethodRegistry.TraceMethod(action = "break_mon_wait", display = "Break on monitor wait", schema = "EventContainer") public void break_mon_wait_container(RmiTraceObject obj) { break_mon_wait(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_wait", - display = "Break on monitor wait", - schema = "ReferenceType") + @RmiMethodRegistry.TraceMethod(action = "break_mon_wait", display = "Break on monitor wait", schema = "ReferenceType") public void break_mon_wait_reftype(RmiTraceObject obj) { break_mon_wait(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_wait", - display = "Break on monitor wait", - schema = "ObjectReference") + @RmiMethodRegistry.TraceMethod(action = "break_mon_wait", display = "Break on monitor wait", schema = "ObjectReference") public void break_mon_wait_instance(RmiTraceObject obj) { break_mon_wait(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_wait", - display = "Break on monitor wait", - schema = "Thread") + @RmiMethodRegistry.TraceMethod(action = "break_mon_wait", display = "Break on monitor wait", schema = "Thread") public void break_mon_wait_thread(RmiTraceObject obj) { break_mon_wait(obj); } private void break_mon_waited(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); - MonitorWaitedRequest brkReq = vm.eventRequestManager() - .createMonitorWaitedRequest(); + MonitorWaitedRequest brkReq = vm.eventRequestManager().createMonitorWaitedRequest(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ReferenceType reftype) { brkReq.putProperty("Class", reftype); @@ -1296,47 +1046,29 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_waited", - display = "Break on monitor waited", - schema = "EventContainer") + @RmiMethodRegistry.TraceMethod(action = "break_mon_waited", display = "Break on monitor waited", schema = "EventContainer") public void break_mon_waited_container(RmiTraceObject obj) { break_mon_waited(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_waited", - display = "Break on monitor waited", - schema = "ReferenceType") + @RmiMethodRegistry.TraceMethod(action = "break_mon_waited", display = "Break on monitor waited", schema = "ReferenceType") public void break_mon_waited_reftype(RmiTraceObject obj) { break_mon_waited(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_waited", - display = "Break on monitor waited", - schema = "ObjectReference") + @RmiMethodRegistry.TraceMethod(action = "break_mon_waited", display = "Break on monitor waited", schema = "ObjectReference") public void break_mon_waited_instance(RmiTraceObject obj) { break_mon_waited(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_waited", - display = "Break on monitor waited", - schema = "Thread") + @RmiMethodRegistry.TraceMethod(action = "break_mon_waited", display = "Break on monitor waited", schema = "Thread") public void break_mon_waited_thread(RmiTraceObject obj) { break_mon_waited(obj); } - @RmiMethodRegistry.TraceMethod( - action = "add_count_filter", - display = "Add count filter", - schema = "Event") + @RmiMethodRegistry.TraceMethod(action = "add_count_filter", display = "Add count filter", schema = "Event") public void add_count_filter(RmiTraceObject obj, - @TargetMethod.Param( - description = "Count", - display = "MaxCount", - name = "count") Integer count) { + @TargetMethod.Param(description = "Count", display = "MaxCount", name = "count") Integer count) { Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof EventRequest req) { req.disable(); @@ -1347,19 +1079,10 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "set_class_filter", - display = "Set class filter", - schema = "Event") + @RmiMethodRegistry.TraceMethod(action = "set_class_filter", display = "Set class filter", schema = "Event") public void set_class_filter(RmiTraceObject obj, - @TargetMethod.Param( - description = "Filter Pattern", - display = "Filter", - name = "filter") String filter, - @TargetMethod.Param( - description = "Exclude", - display = "Exclude", - name = "exclude") String exclude) { + @TargetMethod.Param(description = "Filter Pattern", display = "Filter", name = "filter") String filter, + @TargetMethod.Param(description = "Exclude", display = "Exclude", name = "exclude") String exclude) { Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof MethodEntryRequest req) { req.disable(); @@ -1460,15 +1183,9 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "set_source_filter", - display = "Set source filter", - schema = "Event") + @RmiMethodRegistry.TraceMethod(action = "set_source_filter", display = "Set source filter", schema = "Event") public void set_source_filter(RmiTraceObject obj, - @TargetMethod.Param( - description = "Source Name Pattern", - display = "SourceName", - name = "srcname") String srcname) { + @TargetMethod.Param(description = "Source Name Pattern", display = "SourceName", name = "srcname") String srcname) { Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ClassPrepareRequest req) { req.disable(); @@ -1479,10 +1196,7 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "set_platform_filter", - display = "Set platform filter", - schema = "Event") + @RmiMethodRegistry.TraceMethod(action = "set_platform_filter", display = "Set platform filter", schema = "Event") public void set_platform_filter(RmiTraceObject obj) { Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ThreadStartRequest req) { @@ -1500,16 +1214,13 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "toggle_breakpoint", - display = "Toggle breakpoint", - schema = "BreakpointSpec") + @RmiMethodRegistry.TraceMethod(action = "toggle_breakpoint", display = "Toggle breakpoint", schema = "BreakpointSpec") public void toggle_breakpoint(RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof Field field) { - ModificationWatchpointRequest brkReq = vm.eventRequestManager() - .createModificationWatchpointRequest(field); + ModificationWatchpointRequest brkReq = + vm.eventRequestManager().createModificationWatchpointRequest(field); brkReq.enable(); } if (ctxt instanceof EventRequest req) { @@ -1523,10 +1234,7 @@ public class TraceJdiMethods implements RmiMethods { cmds.putBreakpoints(); } - @RmiMethodRegistry.TraceMethod( - action = "toggle_event", - display = "Toggle event", - schema = "Event") + @RmiMethodRegistry.TraceMethod(action = "toggle_event", display = "Toggle event", schema = "Event") public void toggle_event(RmiTraceObject obj) { Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof EventRequest req) { @@ -1540,10 +1248,7 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "toggle_scope", - display = "Toggle scope", - schema = "MethodContainer") + @RmiMethodRegistry.TraceMethod(action = "toggle_scope", display = "Toggle scope", schema = "MethodContainer") public void toggle_scope_methods(RmiTraceObject obj) { String ppath = cmds.getParentPath(obj.getPath()); Object parent = getObjectFromPath(ppath); @@ -1551,10 +1256,7 @@ public class TraceJdiMethods implements RmiMethods { refresh_methods(obj); } - @RmiMethodRegistry.TraceMethod( - action = "toggle_scope", - display = "Toggle scope", - schema = "FieldContainer") + @RmiMethodRegistry.TraceMethod(action = "toggle_scope", display = "Toggle scope", schema = "FieldContainer") public void toggle_scope_fields(RmiTraceObject obj) { String ppath = cmds.getParentPath(obj.getPath()); Object parent = getObjectFromPath(ppath); diff --git a/Ghidra/Features/Base/src/main/help/help/topics/BookmarkPlugin/Bookmarks.htm b/Ghidra/Features/Base/src/main/help/help/topics/BookmarkPlugin/Bookmarks.htm index f6a703ad73..7f2ec2600f 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/BookmarkPlugin/Bookmarks.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/BookmarkPlugin/Bookmarks.htm @@ -290,7 +290,7 @@ only the entry point bookmarks, you would enter "entry" in the filter field. The results would show only those bookmarks with a Category or Description containing the word "entry". The text filter is not case sensitive, nor does it support regular expressions.

+ "help/topics/Glossary/glossary.htm#RegularExpression">Regular Expressions.

Reorder Columns

diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Glossary/glossary.htm b/Ghidra/Features/Base/src/main/help/help/topics/Glossary/glossary.htm index 3ced47cc76..652ec485a1 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/Glossary/glossary.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/Glossary/glossary.htm @@ -1111,7 +1111,10 @@ xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-

Regular Expression

-

A character sequence used to match patterns in strings.

+

A character sequence used to match patterns in strings. See + Regular Expression + for examples. +

Relocation Table

diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/context/ProgramLocationActionContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/context/ProgramLocationActionContext.java index 0f89d4738d..dce04ed743 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/context/ProgramLocationActionContext.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/context/ProgramLocationActionContext.java @@ -124,7 +124,7 @@ public class ProgramLocationActionContext extends ProgramActionContext return functions; } - private Function getFunctionForLocation() { + protected Function getFunctionForLocation() { if (location instanceof FunctionLocation functionLocation) { Address functionAddress = functionLocation.getFunctionAddress(); return program.getFunctionManager().getFunctionAt(functionAddress); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreePlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreePlugin.java index 381a9a64ad..89e7ae4aff 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreePlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreePlugin.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,6 +25,7 @@ import docking.action.DockingAction; import docking.action.MenuData; import generic.theme.GIcon; import ghidra.app.CorePluginPackage; +import ghidra.app.context.FunctionSupplierContext; import ghidra.app.context.ListingActionContext; import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.ProgramPlugin; @@ -145,7 +146,15 @@ public class CallTreePlugin extends ProgramPlugin { @Override public boolean isAddToPopup(ActionContext context) { - return (context instanceof ListingActionContext); + if (context instanceof ListingActionContext) { + return true; + } + + if (context instanceof FunctionSupplierContext functionContext) { + return functionContext.hasFunctions(); + } + + return false; } }; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferenceContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferenceContext.java index efc85f3b15..1784d1884e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferenceContext.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferenceContext.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -92,6 +92,18 @@ public class LocationReferenceContext { return buffy.toString(); } + /** + * Returns text that is helpful for debugging, such as printing to a console. + * @return the text + */ + public String getDebugText() { + StringBuilder buffy = new StringBuilder(); + for (Part part : parts) { + buffy.append(part.getDebugText()); + } + return buffy.toString(); + } + /** * Returns HTML text for this context. Any matching items embedded in the returned string will * be bold. @@ -142,7 +154,7 @@ public class LocationReferenceContext { abstract String getHtmlText(); - abstract String getText(String start, String end); + abstract String getDebugText(); static String fixBreakingSpaces(String s) { String updated = s.replaceAll("\\s", " "); @@ -165,8 +177,8 @@ public class LocationReferenceContext { } @Override - String getText(String start, String end) { - return text; // we don't decorate + String getDebugText() { + return text; } @Override @@ -187,8 +199,8 @@ public class LocationReferenceContext { } @Override - String getText(String start, String end) { - return start + text + end; + String getDebugText() { + return " [[ " + text + " ]] "; } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferenceContextBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferenceContextBuilder.java index 2240778b50..a99a9d0b9f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferenceContextBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferenceContextBuilder.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -58,6 +58,19 @@ public class LocationReferenceContextBuilder { return this; } + /** + * Adds a newline character to the previously added text. + * @return this builder + */ + public LocationReferenceContextBuilder newline() { + if (parts.isEmpty()) { + throw new IllegalStateException("Cannot add a newline without first appending text"); + } + Part last = parts.get(parts.size() - 1); + last.text += '\n'; + return this; + } + /** * Builds a {@link LocationReferenceContext} using the text supplied via the {@code append} * methods. @@ -67,6 +80,14 @@ public class LocationReferenceContextBuilder { return new LocationReferenceContext(parts); } + /** + * Returns true if no text has been added to this builder. + * @return true if no text has been added to this builder + */ + public boolean isEmpty() { + return parts.isEmpty(); + } + @Override public String toString() { return Json.toString(this); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin.java index eacc2d6ba6..2f78643573 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin.java @@ -150,8 +150,7 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList if (result == null) { searchDialog.setStatusText("Not found"); } - else if (result.programLocation() - .equals(currentLocation)) { + else if (result.programLocation().equals(currentLocation)) { searchNext(searchTask.getProgram(), searchNavigatable, textSearcher); } else { @@ -376,8 +375,9 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList * Create the action for to pop up the search dialog. */ private void createActions() { - String subGroup = getClass().getName(); + String subGroup = "d"; // Memory Search uses groups 'a', 'b', and 'c' + //@formatter:off new ActionBuilder("Search Text", getName()) .menuPath("&Search", "Program &Text...") .menuGroup("search", subGroup) @@ -406,6 +406,7 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList searchDialog.repeatSearch(); }) .buildAndInstall(tool); + //@formatter:on } protected void updateNavigatable(ActionContext context) { @@ -467,8 +468,7 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList String textSelection = navigatable.getTextSelection(); ProgramLocation location = navigatable.getLocation(); Address address = location.getAddress(); - Listing listing = context.getProgram() - .getListing(); + Listing listing = context.getProgram().getListing(); CodeUnit codeUnit = listing.getCodeUnitAt(address); boolean isInstruction = false; if (textSelection != null) { @@ -582,8 +582,7 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList // must have completed too fast for the provider to be set; try something cute Component focusOwner = - KeyboardFocusManager.getCurrentKeyboardFocusManager() - .getFocusOwner(); + KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); return focusOwner; // assume this IS the provider } @@ -628,8 +627,7 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList @Override public Highlight[] createHighlights(String text, ListingField field, int cursorTextOffset) { - Class fieldFactoryClass = field.getFieldFactory() - .getClass(); + Class fieldFactoryClass = field.getFieldFactory().getClass(); if (!doHighlight) { return NO_HIGHLIGHTS; @@ -652,8 +650,7 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList return getAllHighlights(text, cursorTextOffset); } - Address address = searchResult.programLocation() - .getAddress(); + Address address = searchResult.programLocation().getAddress(); ProxyObj proxy = field.getProxy(); if (proxy.contains(address)) { return getSingleSearchHighlight(text, field, cursorTextOffset); @@ -745,8 +742,7 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList return true; } - Class factoryClass = field.getFieldFactory() - .getClass(); + Class factoryClass = field.getFieldFactory().getClass(); if (searchOptions.searchComments()) { if (factoryClass == PreCommentFieldFactory.class || factoryClass == PlateFieldFactory.class || diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java index 8b06b73aaa..ed1bb7bf19 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -384,6 +384,10 @@ public class TableComponentProvider extends ComponentProviderAdapter return model; } + public GhidraTable getTable() { + return threadedPanel.getTable(); + } + private void updateTitle() { setSubTitle(generateSubTitle()); } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/PrettyPrinter.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/PrettyPrinter.java index 79c5977f6d..e0c34c5f80 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/PrettyPrinter.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/PrettyPrinter.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,14 +25,12 @@ import ghidra.program.model.symbol.NameTransformer; import ghidra.util.StringUtilities; /** - * This class is used to convert a C/C++ language - * token group into readable C/C++ code. + * This class is used to convert a C/C++ language token group into readable C/C++ code. */ public class PrettyPrinter { - /** - * The indent string to use when printing. - */ + private final static NameTransformer IDENTITY = new IdentityNameTransformer(); + public final static String INDENT_STRING = " "; private Function function; @@ -51,7 +49,7 @@ public class PrettyPrinter { public PrettyPrinter(Function function, ClangTokenGroup tokgroup, NameTransformer transformer) { this.function = function; this.tokgroup = tokgroup; - this.transformer = (transformer != null) ? transformer : new IdentityNameTransformer(); + this.transformer = transformer != null ? transformer : IDENTITY; flattenLines(); padEmptyLines(); } @@ -72,8 +70,7 @@ public class PrettyPrinter { } /** - * Returns a list of the C language lines contained in the - * C language token group. + * Returns a list of the C language lines contained in the C language token group. * @return a list of the C language lines */ public List getLines() { @@ -81,37 +78,51 @@ public class PrettyPrinter { } /** - * Prints the C language token group - * into a string of C code. + * Prints the C language token group into a string of C code. * @return a string of readable C code */ public DecompiledFunction print() { StringBuilder buff = new StringBuilder(); for (ClangLine line : lines) { - buff.append(line.getIndentString()); - List tokens = line.getAllTokens(); - - for (ClangToken token : tokens) { - boolean isToken2Clean = token instanceof ClangFuncNameToken || - token instanceof ClangVariableToken || token instanceof ClangTypeToken || - token instanceof ClangFieldToken || token instanceof ClangLabelToken; - - //do not clean constant variable tokens - if (isToken2Clean && token.getSyntaxType() == ClangToken.CONST_COLOR) { - isToken2Clean = false; - } - - String tokenText = token.getText(); - if (isToken2Clean) { - tokenText = transformer.simplify(tokenText); - } - buff.append(tokenText); - } + getText(buff, line, transformer); buff.append(StringUtilities.LINE_SEPARATOR); } return new DecompiledFunction(findSignature(), buff.toString()); } + private static void getText(StringBuilder buff, ClangLine line, NameTransformer transformer) { + buff.append(line.getIndentString()); + List tokens = line.getAllTokens(); + + for (ClangToken token : tokens) { + boolean isToken2Clean = token instanceof ClangFuncNameToken || + token instanceof ClangVariableToken || token instanceof ClangTypeToken || + token instanceof ClangFieldToken || token instanceof ClangLabelToken; + + //do not clean constant variable tokens + if (isToken2Clean && token.getSyntaxType() == ClangToken.CONST_COLOR) { + isToken2Clean = false; + } + + String tokenText = token.getText(); + if (isToken2Clean) { + tokenText = transformer.simplify(tokenText); + } + buff.append(tokenText); + } + } + + /** + * Returns the text of the given line as seen in the UI. + * @param line the line + * @return the text + */ + public static String getText(ClangLine line) { + StringBuilder buff = new StringBuilder(); + getText(buff, line, IDENTITY); + return buff.toString(); + } + private String findSignature() { int nChildren = tokgroup.numChildren(); for (int i = 0; i < nChildren; ++i) { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerActionContext.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerActionContext.java index ccde49eb95..ce6c51c881 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerActionContext.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerActionContext.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,6 +23,7 @@ import ghidra.app.context.NavigatableActionContext; import ghidra.app.context.RestrictedAddressSetContext; import ghidra.app.decompiler.*; import ghidra.app.decompiler.component.DecompilerPanel; +import ghidra.app.decompiler.component.DecompilerUtils; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Function; @@ -123,8 +124,8 @@ public class DecompilerActionContext extends NavigatableActionContext if (lineNumber != 0) { return lineNumber; } - getTokenAtCursor(); - return tokenAtCursor == null ? 0 : tokenAtCursor.getLineParent().getLineNumber(); + ClangToken token = getTokenAtCursor(); + return token == null ? 0 : token.getLineParent().getLineNumber(); } public DecompilerPanel getDecompilerPanel() { @@ -152,6 +153,22 @@ public class DecompilerActionContext extends NavigatableActionContext getComponentProvider().getController().setStatusMessage(msg); } + // allows this Decompiler action context to signal the location is on a function + @Override + protected Function getFunctionForLocation() { + ClangToken token = getTokenAtCursor(); + if (token == null) { + return null; + } + + if (token instanceof ClangFuncNameToken functionToken) { + Function function = DecompilerUtils.getFunction(program, functionToken); + return function; + } + + return null; + } + /** * The companion method of {@link #checkActionEnablement(Supplier)}. * diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerProvider.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerProvider.java index a43259d00a..68f33df871 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerProvider.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerProvider.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -1062,7 +1062,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter // // Search // - String searchGroup = "comment2 - Search Group"; + String searchGroup = "Comment2 - Search Group"; subGroupPosition = 0; // reset for the next group FindAction findAction = new FindAction(); diff --git a/Ghidra/Features/DecompilerDependent/build.gradle b/Ghidra/Features/DecompilerDependent/build.gradle index bf9c36055e..9fab3b88c9 100644 --- a/Ghidra/Features/DecompilerDependent/build.gradle +++ b/Ghidra/Features/DecompilerDependent/build.gradle @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,6 +15,7 @@ */ apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle" apply from: "$rootProject.projectDir/gradle/javaProject.gradle" +apply from: "$rootProject.projectDir/gradle/helpProject.gradle" apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle" apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle" apply plugin: 'eclipse' diff --git a/Ghidra/Features/DecompilerDependent/certification.manifest b/Ghidra/Features/DecompilerDependent/certification.manifest index b40809b58c..b428bddc97 100644 --- a/Ghidra/Features/DecompilerDependent/certification.manifest +++ b/Ghidra/Features/DecompilerDependent/certification.manifest @@ -1,3 +1,8 @@ ##VERSION: 2.0 Module.manifest||GHIDRA||||END| data/ExtensionPoint.manifest||GHIDRA||||END| +data/decompiler.dependent.theme.properties||GHIDRA||||END| +src/main/help/help/TOC_Source.xml||GHIDRA||||END| +src/main/help/help/topics/DecompilerTextFinderPlugin/Decompiler_Text_Finder.html||GHIDRA||||END| +src/main/help/help/topics/DecompilerTextFinderPlugin/images/DecompilerTextFinderDialog.png||GHIDRA||||END| +src/main/help/help/topics/DecompilerTextFinderPlugin/images/DecompilerTextFinderResultsTable.png||GHIDRA||||END| diff --git a/Ghidra/Features/DecompilerDependent/data/decompiler.dependent.theme.properties b/Ghidra/Features/DecompilerDependent/data/decompiler.dependent.theme.properties new file mode 100644 index 0000000000..f4b6f297f1 --- /dev/null +++ b/Ghidra/Features/DecompilerDependent/data/decompiler.dependent.theme.properties @@ -0,0 +1,7 @@ + +[Defaults] + + +icon.plugin.decompiler.text.finder.select.functions = icon.make.selection {FunctionScope.gif[size(12,12)][move(6,6)]} + +[Dark Defaults] \ No newline at end of file diff --git a/Ghidra/Features/DecompilerDependent/src/main/help/help/TOC_Source.xml b/Ghidra/Features/DecompilerDependent/src/main/help/help/TOC_Source.xml new file mode 100644 index 0000000000..3ee1228742 --- /dev/null +++ b/Ghidra/Features/DecompilerDependent/src/main/help/help/TOC_Source.xml @@ -0,0 +1,56 @@ + + + + + + + + diff --git a/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTextFinderPlugin/Decompiler_Text_Finder.html b/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTextFinderPlugin/Decompiler_Text_Finder.html new file mode 100644 index 0000000000..c6a52eb4f0 --- /dev/null +++ b/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTextFinderPlugin/Decompiler_Text_Finder.html @@ -0,0 +1,197 @@ + + + + + Search Decompiled Text + + + + + + +

Search Decompiled Text

+ +
+

+ Available from Search -> Search + Decompiled Text, this action allows you to search the decompiled output + for each function in the current program. +

+
+ +
+

+ The Decompiled Function Search dialog is shown when users execute the Search + Decompiled Text action. If String Search is selected, then a case insensitive text + search is performed using the specified text. If Regular Expression is selected, then + a Regular Expression + search is performed. Selecting Search Selection will only search function entry points + that are in the current program selection. +

+ +

+ + + + + + + + +
+
+ The Decompiled Function Search input dialog +
+ + + +
+

NoteWhen restricting the search to + the current program selection, only functions whose entry point is in the selection will + be searched. The selection will not search functions that have a program selection in + the body, but not at the entry point. +

+


+ +
+

NoteBy default text searches apply + only to one line at a time. If you would like a multi-line search, then you will need + to use a Regular Expression search to do so. See the table below for an example. +

+


+ +
+ +

Search Results

+
+

+ The search results will be presented as they are found, as seen in the table below. For single + line search matches, the matching part of the search will be highlighted in the Context + column. +

+ +

+ + + + + + + + +
+
+ The Decompiled Function Search results table +
+ + +

Select Functions Action +

+
+

+ + This action will create a program selection for each function entry point for each function + selected in the table. +

+
+ +

Other Actions

+
+

+ Help for the other table actions can be found + here. +

+
+ +
+ + +

Example Searches

+
+

+ The table below show some example searches and lines that they will match. Note that the + reported matches will show the entire line that matched, not just the matching portion of the + line. +

+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + +
Search StringDescriptionExample Matching Lines
+ + = '\0' + + + A non-regular expression to find the null character assignment. + + + ptr->data[1] = '\0'; + +
+ + set_string\(.*->.*\) + + + A regular expression to find set_string( followed by any number of characters, + followed by ->, followed by any number of characters and a closing parenthesis. + + + set_string(mytable->entry + mytable->numcodes,ptr); + +
+ + (?s)ffff.*piVar2 = + + + A regular expression that searches across multiple lines by using (?S). This + will find ffff followed by any number of characters, followed by + piVar2 =. + + + if (__CTOR_LIST__ != (code *)0xffffffff) {
+ piVar2 = (int *)&__CTOR_LIST__;, +
+
+
+
+ + + + + + + +

Provided by: Decompiler Text Finder Plugin

+ +

Related Topics:

+ +
+
+ + \ No newline at end of file diff --git a/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTextFinderPlugin/images/DecompilerTextFinderDialog.png b/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTextFinderPlugin/images/DecompilerTextFinderDialog.png new file mode 100644 index 0000000000..c9f279e131 Binary files /dev/null and b/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTextFinderPlugin/images/DecompilerTextFinderDialog.png differ diff --git a/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTextFinderPlugin/images/DecompilerTextFinderResultsTable.png b/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTextFinderPlugin/images/DecompilerTextFinderResultsTable.png new file mode 100644 index 0000000000..ecb0f3f16c Binary files /dev/null and b/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTextFinderPlugin/images/DecompilerTextFinderResultsTable.png differ diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/DecompilerTextFinder.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/DecompilerTextFinder.java new file mode 100644 index 0000000000..1329048010 --- /dev/null +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/DecompilerTextFinder.java @@ -0,0 +1,389 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.search; + +import java.util.*; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import generic.json.Json; +import ghidra.app.decompiler.*; +import ghidra.app.decompiler.component.DecompilerUtils; +import ghidra.app.decompiler.parallel.*; +import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext; +import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContextBuilder; +import ghidra.program.model.address.AddressSet; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.listing.*; +import ghidra.util.Msg; +import ghidra.util.task.TaskMonitor; +import utility.function.Dummy; + +/** + * Searches for the given pattern in all functions in the given program. + */ +public class DecompilerTextFinder { + + /** + * Finds text inside decompiled functions using the given pattern. + * @param program the program + * @param searchPattern the search pattern + * @param consumer the consumer that will get matches + * @param monitor the task monitor + * @see #findText(Program, Pattern, Collection, Consumer, TaskMonitor) + */ + public void findText(Program program, Pattern searchPattern, Consumer consumer, + TaskMonitor monitor) { + + monitor = TaskMonitor.dummyIfNull(monitor); + StringFinderCallback callback = new StringFinderCallback(program, searchPattern, consumer); + + Listing listing = program.getListing(); + FunctionIterator functions = listing.getFunctions(true); + + doFindText(program, functions, callback, monitor); + } + + /** + * Finds text inside the given decompiled functions using the given pattern. + * @param program the program + * @param searchPattern the search pattern + * @param functions the functions to search + * @param consumer the consumer that will get matches + * @param monitor the task monitor + * @see #findText(Program, Pattern, Consumer, TaskMonitor) + */ + public void findText(Program program, Pattern searchPattern, Iterator functions, + Consumer consumer, TaskMonitor monitor) { + + monitor = TaskMonitor.dummyIfNull(monitor); + StringFinderCallback callback = new StringFinderCallback(program, searchPattern, consumer); + doFindText(program, functions, callback, monitor); + } + + /** + * Finds text inside the given decompiled functions using the given pattern. + * @param program the program + * @param searchPattern the search pattern + * @param functions the functions to search + * @param consumer the consumer that will get matches + * @param monitor the task monitor + * @see #findText(Program, Pattern, Consumer, TaskMonitor) + */ + public void findText(Program program, Pattern searchPattern, Collection functions, + Consumer consumer, TaskMonitor monitor) { + + monitor = TaskMonitor.dummyIfNull(monitor); + StringFinderCallback callback = new StringFinderCallback(program, searchPattern, consumer); + doFindText(functions, callback, monitor); + } + + private void doFindText(Collection functions, StringFinderCallback callback, + TaskMonitor monitor) { + + try { + ParallelDecompiler.decompileFunctions(callback, functions, monitor); + + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); // reset the flag + if (!monitor.isCancelled()) { + Msg.debug(this, "Interrupted while decompiling functions"); + } + } + catch (Exception e) { + Msg.error(this, "Encountered an exception decompiling functions", e); + } + finally { + callback.dispose(); + } + } + + private void doFindText(Program program, Iterator functions, + StringFinderCallback callback, TaskMonitor monitor) { + + Consumer dummy = Dummy.consumer(); + + try { + ParallelDecompiler.decompileFunctions(callback, program, functions, dummy, monitor); + + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); // reset the flag + if (!monitor.isCancelled()) { + Msg.debug(this, "Interrupted while decompiling functions"); + } + } + catch (Exception e) { + Msg.error(this, "Encountered an exception decompiling functions", e); + } + finally { + callback.dispose(); + } + } + + private static class StringFinderCallback extends DecompilerCallback { + + private Consumer callback; + private Pattern pattern; + private String searchText; + + StringFinderCallback(Program program, Pattern pattern, Consumer callback) { + super(program, new DecompilerConfigurer()); + this.pattern = pattern; + this.callback = callback; + this.searchText = pattern.pattern(); + } + + @Override + public Void process(DecompileResults results, TaskMonitor monitor) throws Exception { + + Function function = results.getFunction(); + if (function.isThunk()) { + return null; + } + + ClangTokenGroup tokens = results.getCCodeMarkup(); + List lines = DecompilerUtils.toLines(tokens); + + // (?s) - enable dot all mode + // (?-s) - disable dot all mode + boolean multiLine = (pattern.flags() & Pattern.DOTALL) == Pattern.DOTALL; + if (multiLine) { + performMultiLineSearch(function, lines); + } + else { + // line-by-line search + for (ClangLine cLine : lines) { + findMatch(function, cLine); + } + } + + return null; + } + + private void performMultiLineSearch(Function function, List lines) { + + // Map characters to lines so we can later translate a character position into the line + // that contains it. Also convert all lines into one big run of text to use with the + // regex. Also, turn the c line into a String for later searching. + StringBuilder buffy = new StringBuilder(); + TreeMap linesRangeMap = new TreeMap<>(); + int pos = 0; + for (ClangLine cLine : lines) { + String text = PrettyPrinter.getText(cLine); + buffy.append(text).append('\n'); + TextLine textLine = new TextLine(pos, cLine, text); + linesRangeMap.put(pos, textLine); + pos += text.length() + 1; // +1 for newline + } + + Matcher matcher = pattern.matcher(buffy); + findMatches(function, linesRangeMap, matcher); + } + + /** + * Uses the given matcher to search for all matches, which may span multiple lines. + */ + private void findMatches(Function function, TreeMap linesRangeMap, + Matcher matcher) { + + while (matcher.find()) { + emitTextMatch(function, linesRangeMap, matcher); + } + } + + private void emitTextMatch(Function function, TreeMap linesRangeMap, + Matcher matcher) { + + List lineMatches = new ArrayList<>(); + + int pos = 0; + int start = matcher.start(); + int end = matcher.end(); + for (pos = start; pos < end; pos++) { + + // grab the line that contains the current character position + TextLine line = linesRangeMap.floorEntry(pos).getValue(); + + // This will be positive if the current line contains the match start character. + // In this case, let the line know it has the start. If we don't set the start, + // then the line match will start at 0. + int lineStartOffset = start - line.getOffset(); // relative offset + if (lineStartOffset >= 0) { + line.setMatchStart(lineStartOffset); + } + + // In this case, let the line know it has the end. If we don't set the end, + // then the line match will end at the end of the line. + if (end <= line.getEndOffset()) { + int relativeEnd = end - line.getOffset(); + line.setMatchEnd(relativeEnd); + } + + lineMatches.add(line); + pos = line.getEndOffset(); + } + + // Use the first line for attributes of the match, like line number and address + TextLine firstLine = lineMatches.get(0); + int lineNumber = firstLine.getLineNumber(); + AddressSet addresses = getAddresses(function, firstLine.getCLine()); + LocationReferenceContext context = createMatchContext(lineMatches); + TextMatch match = + new TextMatch(function, addresses, lineNumber, searchText, context, true); + callback.accept(match); + } + + private LocationReferenceContext createMatchContext(List matches) { + + LocationReferenceContextBuilder builder = new LocationReferenceContextBuilder(); + for (TextLine line : matches) { + if (!builder.isEmpty()) { + builder.newline(); + } + + String text = line.getText(); + int start = line.getMatchStart(); + int end = line.getMatchEnd(); + builder.append(text.substring(0, start)); + builder.appendMatch(text.substring(start, end)); + builder.append(text.substring(end, line.length())); + } + + return builder.build(); + } + + private void findMatch(Function function, ClangLine line) { + + String textLine = PrettyPrinter.getText(line); + Matcher matcher = pattern.matcher(textLine); + if (!matcher.find()) { + return; + } + + LocationReferenceContextBuilder builder = new LocationReferenceContextBuilder(); + + int start = matcher.start(); + int end = matcher.end(); + builder.append(textLine.substring(0, start)); + builder.appendMatch(textLine.substring(start, end)); + if (end < textLine.length()) { + builder.append(textLine.substring(end)); + } + + int lineNumber = line.getLineNumber(); + AddressSet addresses = getAddresses(function, line); + LocationReferenceContext context = builder.build(); + TextMatch match = + new TextMatch(function, addresses, lineNumber, searchText, context, false); + callback.accept(match); + } + + private AddressSet getAddresses(Function function, ClangLine line) { + Program program = function.getProgram(); + AddressSpace space = function.getEntryPoint().getAddressSpace(); + List tokens = line.getAllTokens(); + return DecompilerUtils.findClosestAddressSet(program, space, tokens); + } + + /** + * A text line represents a ClangLine, it's pretty text, its character position in the + * overall body of text and the character positions of the portion of text that has matched + * a search. A line may have a search match that is partial or the entire line may match + * the search, such as in a multi-line match. + */ + private class TextLine { + private ClangLine cLine; + private int offset; // the character offset into the entire body of text + private String text; + + // relative offsets + private int matchStart; + private int matchEnd; + + TextLine(int offset, ClangLine cLine, String text) { + this.cLine = cLine; + this.offset = offset; + this.text = text; + + matchStart = 0; + matchEnd = text.length(); + } + + ClangLine getCLine() { + return cLine; + } + + int length() { + return text.length(); + } + + int getOffset() { + return offset; + } + + int getEndOffset() { + return offset + length(); + } + + int getLineNumber() { + return cLine.getLineNumber(); + } + + String getText() { + return text; + } + + int getMatchStart() { + return matchStart; + } + + int getMatchEnd() { + return matchEnd; + } + + void setMatchStart(int matchStart) { + this.matchStart = matchStart; + } + + void setMatchEnd(int matchEnd) { + this.matchEnd = matchEnd; + } + + @Override + public String toString() { + return Json.toString(this); + } + } + } + + private static class DecompilerConfigurer implements DecompileConfigurer { + + @Override + public void configure(DecompInterface decompiler) { + decompiler.toggleCCode(true); + decompiler.toggleSyntaxTree(true); + decompiler.setSimplificationStyle("decompile"); + + DecompileOptions xmlOptions = new DecompileOptions(); + xmlOptions.setDefaultTimeout(60); + decompiler.setOptions(xmlOptions); + } + } + +} diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/DecompilerTextFinderDialog.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/DecompilerTextFinderDialog.java new file mode 100644 index 0000000000..2ac4c02df1 --- /dev/null +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/DecompilerTextFinderDialog.java @@ -0,0 +1,235 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.search; + +import java.awt.BorderLayout; +import java.awt.event.KeyEvent; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import org.apache.commons.lang3.StringUtils; + +import docking.ReusableDialogComponentProvider; +import docking.widgets.checkbox.GCheckBox; +import docking.widgets.combobox.GhidraComboBox; +import docking.widgets.label.GLabel; +import ghidra.util.HelpLocation; +import ghidra.util.MessageType; + +/** + * A dialog to gather input for performing a search over decompiled text. + */ +public class DecompilerTextFinderDialog extends ReusableDialogComponentProvider { + + private GhidraComboBox textCombo; + + private JButton searchButton; + private JCheckBox regexCb; + private JCheckBox searchSelectionCb; + + private String searchText; + private boolean isCancelled; + + public DecompilerTextFinderDialog() { + super("Decompiled Function Search"); + + addWorkPanel(buildMainPanel()); + buildButtons(); + + setHelpLocation(new HelpLocation("DecompilerTextFinderPlugin", "Search_Decompiled_Text")); + } + + private void buildButtons() { + searchButton = new JButton("Search"); + searchButton.addActionListener(ev -> doSearch()); + addButton(searchButton); + setDefaultButton(searchButton); + + addCancelButton(); + } + + private JPanel buildMainPanel() { + + regexCb = new GCheckBox("Regular Expression", false); + regexCb.setName("Regular Expression Search"); + + regexCb.addItemListener(e -> clearStatusText()); + + searchSelectionCb = new JCheckBox("Search Selection"); + searchSelectionCb.setName("Search Selection"); + + textCombo = new GhidraComboBox<>(); + textCombo.setEditable(true); + textCombo.addActionListener(e -> doSearch()); + + textCombo.setColumns(20); + textCombo.addDocumentListener(new DocumentListener() { + @Override + public void changedUpdate(DocumentEvent e) { + handleDocumentUpdate(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + handleDocumentUpdate(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + handleDocumentUpdate(); + } + + private void handleDocumentUpdate() { + String text = textCombo.getText(); + searchButton.setEnabled(!StringUtils.isBlank(text)); + clearStatusText(); + } + }); + + JLabel findLabel = new GLabel("Find: "); + + // associate this label with a mnemonic key that activates the text field + findLabel.setDisplayedMnemonic(KeyEvent.VK_N); + textCombo.associateLabel(findLabel); + + JPanel mainPanel = new JPanel(new BorderLayout()); + JPanel textPanel = new JPanel(); + textPanel.setLayout(new BoxLayout(textPanel, BoxLayout.LINE_AXIS)); + textPanel.add(findLabel); + textPanel.add(textCombo); + mainPanel.add(textPanel, BorderLayout.NORTH); + mainPanel.add(buildOptionsPanel(), BorderLayout.SOUTH); + mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + return mainPanel; + } + + private JPanel buildOptionsPanel() { + JPanel optionsPanel = new JPanel(); + optionsPanel.setLayout(new BoxLayout(optionsPanel, BoxLayout.LINE_AXIS)); + optionsPanel.setBorder(BorderFactory.createTitledBorder("Options")); + + optionsPanel.add(regexCb); + optionsPanel.add(Box.createHorizontalGlue()); + optionsPanel.add(searchSelectionCb); + + return optionsPanel; + } + + @Override + public void close() { + textCombo.setText(""); + super.close(); + } + + private void doSearch() { + + searchText = null; + clearStatusText(); + if (!searchButton.isEnabled()) { + return; // don't search while disabled + } + + String text = textCombo.getText(); + if (!validateRegex(text)) { + return; // leave the dialog open so the user can see the error text + } + + isCancelled = false; + searchText = text; + updateSearchHistory(searchText); + close(); + } + + private boolean validateRegex(String text) { + + if (!isRegex()) { + return true; + } + + try { + Pattern.compile(text); + return true; + } + catch (PatternSyntaxException e) { + setStatusText("Invalid regex: " + e.getMessage(), MessageType.ERROR); + return false; + } + } + + @Override + protected void dialogShown() { + searchButton.setEnabled(false); + clearStatusText(); + searchText = null; + + // To track cancelled, assume that the dialog is always in a cancelled state unless the + // user actually performed a search. + isCancelled = true; + } + + public void setSearchText(String text) { + textCombo.setText(text); + } + + public String getSearchText() { + return searchText; + } + + public boolean isSearchSelection() { + return searchSelectionCb.isSelected(); + } + + public void setSearchSelectionEnabled(boolean b) { + if (!b) { + searchSelectionCb.setEnabled(false); + searchSelectionCb.setSelected(false); + } + else { + searchSelectionCb.setEnabled(true); + } + } + + public boolean isRegex() { + return regexCb.isSelected(); + } + + public boolean isCancelled() { + return isCancelled; + } + + private void updateSearchHistory(String text) { + + MutableComboBoxModel model = (MutableComboBoxModel) textCombo.getModel(); + model.insertElementAt(text, 0); + + int size = model.getSize(); + for (int i = 1; i < size; i++) { + String element = model.getElementAt(i); + if (element.equals(text)) { // already in the list, remove it + model.removeElementAt(i); + break; + } + } + + // do this last since removing items may change the selected item + model.setSelectedItem(text); + } +} diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/DecompilerTextFinderPlugin.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/DecompilerTextFinderPlugin.java new file mode 100644 index 0000000000..9fffe608be --- /dev/null +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/DecompilerTextFinderPlugin.java @@ -0,0 +1,167 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.search; + +import java.awt.Color; + +import javax.swing.Icon; + +import docking.action.MenuData; +import docking.action.builder.ActionBuilder; +import generic.theme.GIcon; +import ghidra.app.CorePluginPackage; +import ghidra.app.context.NavigatableActionContext; +import ghidra.app.context.NavigatableContextAction; +import ghidra.app.events.ProgramSelectionPluginEvent; +import ghidra.app.nav.Navigatable; +import ghidra.app.plugin.PluginCategoryNames; +import ghidra.app.plugin.ProgramPlugin; +import ghidra.app.plugin.core.table.TableComponentProvider; +import ghidra.app.util.SearchConstants; +import ghidra.app.util.query.TableService; +import ghidra.framework.options.ToolOptions; +import ghidra.framework.plugintool.*; +import ghidra.framework.plugintool.util.PluginStatus; +import ghidra.program.model.address.AddressSet; +import ghidra.program.model.listing.*; +import ghidra.program.util.ProgramSelection; +import ghidra.util.table.GhidraTable; + +//@formatter:off +@PluginInfo( + status = PluginStatus.RELEASED, + packageName = CorePluginPackage.NAME, + category = PluginCategoryNames.SEARCH, + shortDescription = "Decompiler Text Finder Plugin", + description = "This plugin adds an action to allow users to search decompiled text.", + servicesRequired = { TableService.class } +) +//@formatter:on +public class DecompilerTextFinderPlugin extends ProgramPlugin { + + private static final Icon SELECT_FUNCTIONS_ICON = + new GIcon("icon.plugin.decompiler.text.finder.select.functions"); + + private DecompilerTextFinderDialog searchDialog; + + public DecompilerTextFinderPlugin(PluginTool tool) { + super(tool); + + createActions(); + } + + private void createActions() { + + NavigatableContextAction searchAction = + new NavigatableContextAction("Search Decompiled Text", getName()) { + @Override + public void actionPerformed(NavigatableActionContext context) { + search(context.getNavigatable(), context.getProgram()); + } + }; + + // Memory Search uses groups 'a', 'b', and 'c'; Search Text uses group 'd' + String subGroup = "e"; + searchAction.setMenuBarData(new MenuData(new String[] { "Search", "Decompiled Text..." }, + null, "search", -1, subGroup)); + + searchAction.addToWindowWhen(NavigatableActionContext.class); + + tool.addAction(searchAction); + } + + private void search(Navigatable navigatable, Program program) { + + if (searchDialog == null) { + searchDialog = new DecompilerTextFinderDialog(); + } + + // update the Search Selection checkbox as needed + boolean enableSearchSelection = false; + if (currentSelection != null) { + FunctionManager functionManager = program.getFunctionManager(); + FunctionIterator it = functionManager.getFunctions(currentSelection, true); + if (it.hasNext()) { + enableSearchSelection = true; + } + } + searchDialog.setSearchSelectionEnabled(enableSearchSelection); + + tool.showDialog(searchDialog); + if (searchDialog.isCancelled()) { + return; + } + + String searchText = searchDialog.getSearchText(); + boolean isRegex = searchDialog.isRegex(); + String title = "Decompiler Search Text - '" + searchText + "'"; + String tableTypeName = "Decompiler Search"; + DecompilerTextFinderTableModel model = + new DecompilerTextFinderTableModel(tool, program, searchText, isRegex); + if (searchDialog.isSearchSelection()) { + model.setSelection(currentSelection); + } + + int searchLimit = getSearchLimit(); + model.setSearchLimit(searchLimit); + + Color markerColor = SearchConstants.SEARCH_HIGHLIGHT_COLOR; + Icon markerIcon = new GIcon("icon.base.search.marker"); + String windowSubMenu = "Search"; + TableService tableService = tool.getService(TableService.class); + TableComponentProvider provider = tableService.showTableWithMarkers(title, + tableTypeName, model, markerColor, markerIcon, windowSubMenu, navigatable); + + provider.installRemoveItemsAction(); + + GhidraTable table = provider.getTable(); + //@formatter:off + new ActionBuilder("Select Functions", getName()) + .description("Make program selection of function starts from selected rows") + .toolBarIcon(SELECT_FUNCTIONS_ICON) + .popupMenuIcon(SELECT_FUNCTIONS_ICON) + .popupMenuPath("Select Functions") + .popupMenuGroup(null, "a") // before the others in the table, to match the toolbar + .enabledWhen(c -> table.getSelectedRowCount() > 0) + .onAction(c -> selectFunctions(table, model)) + .buildAndInstallLocal(provider) + ; + //@formatter:on + } + + private int getSearchLimit() { + ToolOptions options = tool.getOptions(SearchConstants.SEARCH_OPTION_NAME); + return options.getInt(SearchConstants.SEARCH_LIMIT_NAME, + SearchConstants.DEFAULT_SEARCH_LIMIT); + } + + private void selectFunctions(GhidraTable table, DecompilerTextFinderTableModel model) { + + AddressSet addresses = new AddressSet(); + int[] rows = table.getSelectedRows(); + for (int row : rows) { + TextMatch match = model.getRowObject(row); + Function f = match.getFunction(); + addresses.add(f.getEntryPoint()); + } + + ProgramSelection selection = new ProgramSelection(addresses); + PluginEvent event = + new ProgramSelectionPluginEvent(getName(), selection, table.getProgram()); + firePluginEvent(event); + } + +} diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/DecompilerTextFinderTableModel.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/DecompilerTextFinderTableModel.java new file mode 100644 index 0000000000..c8b068265d --- /dev/null +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/DecompilerTextFinderTableModel.java @@ -0,0 +1,200 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.search; + +import java.awt.Component; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.regex.Pattern; + +import docking.widgets.table.*; +import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext; +import ghidra.docking.settings.Settings; +import ghidra.framework.plugintool.ServiceProvider; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.listing.*; +import ghidra.util.datastruct.Accumulator; +import ghidra.util.exception.CancelledException; +import ghidra.util.table.GhidraProgramTableModel; +import ghidra.util.table.column.AbstractGhidraColumnRenderer; +import ghidra.util.table.column.GColumnRenderer; +import ghidra.util.table.field.AbstractProgramBasedDynamicTableColumn; +import ghidra.util.table.field.FunctionNameTableColumn; +import ghidra.util.task.TaskMonitor; + +public class DecompilerTextFinderTableModel extends GhidraProgramTableModel { + + private String searchText; + private boolean isRegex; + private AddressSetView selection; + private int searchLimit; + + protected DecompilerTextFinderTableModel(ServiceProvider serviceProvider, Program program, + String searchText, boolean isRegex) { + super("Decompiler Search", serviceProvider, program, null, true); + this.searchText = searchText; + this.isRegex = isRegex; + } + + void setSelection(AddressSetView selection) { + this.selection = selection; + } + + void setSearchLimit(int limit) { + this.searchLimit = limit; + } + + @Override + protected TableColumnDescriptor createTableColumnDescriptor() { + + TableColumnDescriptor descriptor = new TableColumnDescriptor<>(); + + descriptor.addVisibleColumn( + DiscoverableTableUtils.adaptColumForModel(this, new FunctionNameTableColumn()), 1, + true); + descriptor.addVisibleColumn(new LineNumberTableColumn(), 2, true); + descriptor.addVisibleColumn(new ContextTableColumn()); + + return descriptor; + } + + @Override + protected void doLoad(Accumulator accumulator, TaskMonitor monitor) + throws CancelledException { + + // Add a consumer that will monitor the count and stop the process on too many results + AtomicInteger counter = new AtomicInteger(); + Consumer limitedConsumer = tm -> { + int count = counter.incrementAndGet(); + if (count >= searchLimit) { + monitor.cancel(); + } + + accumulator.add(tm); + }; + + Pattern pattern; + if (isRegex) { + // note: we expect this to be a valid regex + pattern = Pattern.compile(searchText); + } + else { + String quoted = Pattern.quote(searchText); + pattern = Pattern.compile(quoted, Pattern.CASE_INSENSITIVE); + } + + DecompilerTextFinder finder = new DecompilerTextFinder(); + if (selection != null) { + FunctionManager functionManager = program.getFunctionManager(); + FunctionIterator functions = functionManager.getFunctions(selection, true); + finder.findText(program, pattern, functions, limitedConsumer, monitor); + } + else { + finder.findText(program, pattern, limitedConsumer, monitor); + } + } + + @Override + public Address getAddress(int row) { + TextMatch match = getRowObject(row); + if (match != null) { + return match.getAddress(); + } + return null; + } + +//================================================================================================= +//Inner Classes +//================================================================================================= + + private class LineNumberTableColumn + extends AbstractProgramBasedDynamicTableColumn { + + @Override + public Integer getValue(TextMatch rowObject, Settings settings, Program p, + ServiceProvider sp) throws IllegalArgumentException { + return rowObject.getLineNumber(); + } + + @Override + public String getColumnName() { + return "Line Number"; + } + + @Override + public int getColumnPreferredWidth() { + return 75; + } + } + + private class ContextTableColumn + extends AbstractProgramBasedDynamicTableColumn { + + private ContextCellRenderer renderer = new ContextCellRenderer(); + + @Override + public LocationReferenceContext getValue(TextMatch rowObject, Settings settings, Program p, + ServiceProvider sp) throws IllegalArgumentException { + return rowObject.getContext(); + } + + @Override + public String getColumnName() { + return "Context"; + } + + @Override + public GColumnRenderer getColumnRenderer() { + return renderer; + } + } + + private class ContextCellRenderer + extends AbstractGhidraColumnRenderer { + + { + // the context uses html + setHTMLRenderingEnabled(true); + } + + @Override + public Component getTableCellRendererComponent(GTableCellRenderingData data) { + + // initialize + super.getTableCellRendererComponent(data); + + TextMatch match = (TextMatch) data.getRowObject(); + LocationReferenceContext context = match.getContext(); + String text; + if (match.isMultiLine()) { + // multi-line matches create visual noise when showing colors, as of much of the + // entire line matches + text = context.getPlainText(); + } + else { + text = context.getBoldMatchingText(); + } + setText(text); + return this; + } + + @Override + public String getFilterString(LocationReferenceContext context, Settings settings) { + return context.getPlainText(); + } + } +} diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/TextMatch.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/TextMatch.java new file mode 100644 index 0000000000..094e04a928 --- /dev/null +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/TextMatch.java @@ -0,0 +1,79 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.search; + +import generic.json.Json; +import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSet; +import ghidra.program.model.listing.Function; + +/** + * A simple class that represents text found in Decompiler output. + */ +public class TextMatch { + + private Function function; + private AddressSet addresses; + private LocationReferenceContext context; + private int lineNumber; + + private String searchText; + private boolean isMultiLine; + + TextMatch(Function function, AddressSet addresses, int lineNumber, String searchText, + LocationReferenceContext context, boolean isMultiLine) { + this.function = function; + this.addresses = addresses; + this.lineNumber = lineNumber; + this.searchText = searchText; + this.context = context; + this.isMultiLine = isMultiLine; + } + + public Function getFunction() { + return function; + } + + public LocationReferenceContext getContext() { + return context; + } + + public int getLineNumber() { + return lineNumber; + } + + public Address getAddress() { + if (addresses.isEmpty()) { + return function.getEntryPoint(); + } + + return addresses.getFirstRange().getMinAddress(); + } + + public boolean isMultiLine() { + return isMultiLine; + } + + public String getSearchText() { + return searchText; + } + + @Override + public String toString() { + return Json.toString(this, "function", "context", "searchText"); + } +} diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/TextMatchToAddressTableRowMapper.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/TextMatchToAddressTableRowMapper.java new file mode 100644 index 0000000000..f025ca0bea --- /dev/null +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/TextMatchToAddressTableRowMapper.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.search; + +import ghidra.framework.plugintool.ServiceProvider; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.Program; +import ghidra.util.table.ProgramLocationTableRowMapper; + +public class TextMatchToAddressTableRowMapper + extends ProgramLocationTableRowMapper { + + @Override + public Address map(TextMatch rowObject, Program data, ServiceProvider serviceProvider) { + return rowObject.getAddress(); + } +} diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/TextMatchToFunctionTableRowMapper.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/TextMatchToFunctionTableRowMapper.java new file mode 100644 index 0000000000..ba5023d7ae --- /dev/null +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/search/TextMatchToFunctionTableRowMapper.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.search; + +import ghidra.framework.plugintool.ServiceProvider; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.Program; +import ghidra.util.table.ProgramLocationTableRowMapper; + +public class TextMatchToFunctionTableRowMapper + extends ProgramLocationTableRowMapper { + + @Override + public Function map(TextMatch rowObject, Program data, ServiceProvider serviceProvider) { + return rowObject.getFunction(); + } +} diff --git a/Ghidra/Features/DecompilerDependent/src/test/java/ghidra/app/plugin/core/string/variadic/FormatStringParserTest.java b/Ghidra/Features/DecompilerDependent/src/test/java/ghidra/app/plugin/core/string/variadic/FormatStringParserTest.java index aff7b2564c..68a5664f35 100644 --- a/Ghidra/Features/DecompilerDependent/src/test/java/ghidra/app/plugin/core/string/variadic/FormatStringParserTest.java +++ b/Ghidra/Features/DecompilerDependent/src/test/java/ghidra/app/plugin/core/string/variadic/FormatStringParserTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,7 +23,8 @@ import org.junit.Before; import org.junit.Test; import generic.test.AbstractGenericTest; -import ghidra.program.database.*; +import ghidra.program.database.ProgramBuilder; +import ghidra.program.database.ProgramDB; import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.model.data.*; @@ -81,14 +82,14 @@ public class FormatStringParserTest extends AbstractGenericTest { runFormatTest("%d%hi", expectedTypes2, false); DataType[] expectedTypes3 = - { program.getDataTypeManager().getPointer(new PointerDataType(DataType.VOID)), + { program.getDataTypeManager().getPointer(new PointerDataType(VoidDataType.dataType)), program.getDataTypeManager().getPointer(new CharDataType()) }; runFormatTest("%p%*d%s", expectedTypes3, false); - DataType[] expectedTypes4 = - { program.getDataTypeManager().getPointer(new LongDoubleDataType()), - program.getDataTypeManager().getPointer(new CharDataType()), - program.getDataTypeManager().getPointer(new PointerDataType(DataType.VOID)) }; + DataType[] expectedTypes4 = { + program.getDataTypeManager().getPointer(new LongDoubleDataType()), + program.getDataTypeManager().getPointer(new CharDataType()), + program.getDataTypeManager().getPointer(new PointerDataType(VoidDataType.dataType)) }; runFormatTest("!:%12La%*d+=%2s%3p%*20d", expectedTypes4, false); @@ -106,8 +107,8 @@ public class FormatStringParserTest extends AbstractGenericTest { { program.getDataTypeManager().getPointer(new CharDataType()), new LongDataType() }; runFormatTest("#thisisatest%+-4.12s%#.1lin\nd2", expectedTypes2, true); - DataType[] expectedTypes3 = { new PointerDataType(DataType.VOID), new LongDoubleDataType(), - new UnsignedCharDataType() }; + DataType[] expectedTypes3 = { new PointerDataType(VoidDataType.dataType), + new LongDoubleDataType(), new UnsignedCharDataType() }; runFormatTest("%01.3pp%%%#1.2Lg%%%%%hhXxn2", expectedTypes3, true); DataType[] expectedTypes4 = { new IntegerDataType(), new IntegerDataType(), diff --git a/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java b/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java index 8bd435c985..191a2ffc4e 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -501,12 +501,18 @@ public abstract class DockingAction implements DockingActionIf { // menu path if (menuBarData != null) { - buffer.append(" MENU PATH: ").append( - menuBarData.getMenuPathAsString()); + buffer.append(" MENU PATH: ") + .append(menuBarData.getMenuPathAsString()); buffer.append('\n'); buffer.append(" MENU GROUP: ").append(menuBarData.getMenuGroup()); buffer.append('\n'); + String menuSubGroup = menuBarData.getMenuSubGroup(); + if (menuSubGroup != null) { + buffer.append(" MENU SUB-GROUP: ").append(menuSubGroup); + buffer.append('\n'); + } + String parentGroup = menuBarData.getParentMenuGroup(); if (parentGroup != null) { buffer.append(" PARENT GROUP: ").append(parentGroup); @@ -528,8 +534,8 @@ public abstract class DockingAction implements DockingActionIf { // popup menu path if (popupMenuData != null) { - buffer.append(" POPUP PATH: ").append( - popupMenuData.getMenuPathAsString()); + buffer.append(" POPUP PATH: ") + .append(popupMenuData.getMenuPathAsString()); buffer.append('\n'); buffer.append(" POPUP GROUP: ").append(popupMenuData.getMenuGroup()); buffer.append('\n'); diff --git a/Ghidra/Framework/Generic/src/main/java/generic/json/Json.java b/Ghidra/Framework/Generic/src/main/java/generic/json/Json.java index eadfecf2c9..263e1916fc 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/json/Json.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/json/Json.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,8 +15,11 @@ */ package generic.json; +import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; import org.apache.commons.lang3.builder.*; @@ -135,16 +138,14 @@ public class Json extends ToStringStyle { * @return the string */ public static String toStringExclude(Object o, String... excludedFields) { - ReflectionToStringBuilder builder = new ReflectionToStringBuilder(o, - Json.WITH_NEWLINES); + ReflectionToStringBuilder builder = new ReflectionToStringBuilder(o, Json.WITH_NEWLINES); builder.setExcludeFieldNames(excludedFields); return builder.toString(); } - // Future: update this class to use the order of the included fields to be the printed ordered private static class InclusiveReflectionToStringBuilder extends ReflectionToStringBuilder { - private String[] includedNames; + private String[] includedNames = new String[0]; public InclusiveReflectionToStringBuilder(Object object) { super(object, WITH_NEWLINES); @@ -156,27 +157,73 @@ public class Json extends ToStringStyle { return false; } - if (this.includedNames != null && - Arrays.binarySearch(this.includedNames, field.getName()) >= 0) { - return true; + if (includedNames.length == 0) { + return true; // this implies a programming error + } + + String fieldName = field.getName(); + for (String name : includedNames) { + if (fieldName.equals(name)) { + return true; + } } return false; } + // Overridden to control the order the field are listed. The parent class sorts by name; we + // want output in the order specified by the user. + @Override + protected void appendFieldsIn(final Class clazz) { + if (clazz.isArray()) { + super.appendFieldsIn(clazz); + return; + } + + if (includedNames.length == 0) { + super.appendFieldsIn(clazz); + return; + } + + Field[] fields = clazz.getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + Map fieldsByName = + Arrays.stream(fields).collect(Collectors.toMap(f -> f.getName(), f -> f)); + for (String name : includedNames) { + + Field field = fieldsByName.get(name); + if (field == null) { + continue; + } + + if (accept(field)) { + try { + // Field.get(Object) creates wrappers objects for primitive types. + Object fieldValue = this.getValue(field); + if (!isExcludeNullValues() || fieldValue != null) { + this.append(name, fieldValue, + !field.isAnnotationPresent(ToStringSummary.class)); + } + } + catch (IllegalAccessException ex) { + throw new InternalError( + "Unexpected IllegalAccessException: " + ex.getMessage()); + } + } + } + } + /** * Sets the names to be included * @param includeFieldNamesParam the names * @return this builder */ - public ReflectionToStringBuilder setIncludeFieldNames( - final String... includeFieldNamesParam) { + public ReflectionToStringBuilder setIncludeFieldNames(String... includeFieldNamesParam) { if (includeFieldNamesParam == null) { - this.includedNames = null; + this.includedNames = new String[0]; } else { this.includedNames = includeFieldNamesParam; - Arrays.sort(this.includedNames); } return this; } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/Accumulator.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/Accumulator.java index 4f1058d614..0915871d53 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/Accumulator.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/Accumulator.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,6 +16,7 @@ package ghidra.util.datastruct; import java.util.Collection; +import java.util.function.Consumer; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -31,7 +32,7 @@ import java.util.stream.StreamSupport; * * @param the type */ -public interface Accumulator extends Iterable { +public interface Accumulator extends Iterable, Consumer { public void add(T t); @@ -50,4 +51,9 @@ public interface Accumulator extends Iterable { default Stream stream() { return StreamSupport.stream(spliterator(), false); } + + @Override + default void accept(T t) { + add(t); + } } diff --git a/Ghidra/Framework/Utility/src/main/java/ghidra/util/UserSearchUtils.java b/Ghidra/Framework/Utility/src/main/java/ghidra/util/UserSearchUtils.java index 1913274ddc..fe7bc7cbb8 100644 --- a/Ghidra/Framework/Utility/src/main/java/ghidra/util/UserSearchUtils.java +++ b/Ghidra/Framework/Utility/src/main/java/ghidra/util/UserSearchUtils.java @@ -305,10 +305,11 @@ public class UserSearchUtils { */ private static String convertUserInputToRegex(String input, boolean allowGlobbing) { - // Note: Order is important! (due to how escape characters added and checked) - String escaped = escapeEscapeCharacters(input); - + String escaped = input; if (allowGlobbing) { + + // Note: Order is important! (due to how escape characters added and checked) + escaped = escapeEscapeCharacters(input); escaped = escapeNonGlobbingRegexCharacters(input); escaped = convertGlobbingCharactersToRegex(escaped); } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DecompilerTextFinderPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DecompilerTextFinderPluginScreenShots.java new file mode 100644 index 0000000000..8421afc59e --- /dev/null +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DecompilerTextFinderPluginScreenShots.java @@ -0,0 +1,59 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package help.screenshot; + +import org.junit.Test; + +import docking.action.DockingActionIf; +import ghidra.app.plugin.core.search.DecompilerTextFinderDialog; +import ghidra.app.plugin.core.search.TextMatch; +import ghidra.app.plugin.core.table.TableComponentProvider; +import ghidra.util.table.GhidraProgramTableModel; + +public class DecompilerTextFinderPluginScreenShots extends GhidraScreenShotGenerator { + + @Test + public void testDecompilerTextFinderDialog() { + + DockingActionIf action = getAction(tool, "Search Decompiled Text"); + performAction(action, false); + captureDialog(); + closeAllWindows(); + } + + @Test + public void testDecompilerTextFinderResultsTable() { + + DockingActionIf action = getAction(tool, "Search Decompiled Text"); + performAction(action, false); + DecompilerTextFinderDialog searchDialog = + waitForDialogComponent(DecompilerTextFinderDialog.class); + + String searchText = " = '\\0'"; + runSwing(() -> searchDialog.setSearchText(searchText)); + pressButtonByText(searchDialog, "Search", false); + + @SuppressWarnings("unchecked") + TableComponentProvider tableProvider = + waitForComponentProvider(TableComponentProvider.class); + GhidraProgramTableModel model = tableProvider.getModel(); + waitForTableModel(model); + + // TOD capture entire window? + captureProvider(tableProvider); + close(searchDialog); + } +}