Compare commits

...

38 commits

Author SHA1 Message Date
Ryan Kurtz
e4e2e4369e Merge remote-tracking branch 'origin/patch' 2025-08-20 07:59:09 -04:00
Ryan Kurtz
2e0e64b7c6 Merge branch 'GP-5648_ryanmkurtz_cert-headers' into patch 2025-08-20 06:17:17 -04:00
Ryan Kurtz
cdc5af10e4 GP-5648: Certification header support for .bat and .ps1 files 2025-08-20 05:58:46 -04:00
Ryan Kurtz
c5d0f6925b Merge remote-tracking branch 'origin/GP-5937-dragonmacher-symbol-tree-fixes' 2025-08-20 05:15:18 -04:00
Ryan Kurtz
384d72782a Merge remote-tracking branch 'origin/patch' 2025-08-20 05:12:40 -04:00
Ryan Kurtz
154aa4460c Merge remote-tracking branch 'origin/GP-0-dragonmacher-test-fixes-8-19-25-2' into patch 2025-08-20 05:10:24 -04:00
ghidra1
d38f512437 GP-1 Added ability to skip building of all natives for gradle build 2025-08-19 18:08:30 -04:00
dragonmacher
81fce4fed5 Fixes for Symbol Tree exceptions 2025-08-19 17:05:56 -04:00
dragonmacher
3e240563ab Test debug 2025-08-19 16:49:40 -04:00
Ryan Kurtz
4172e448dd Merge remote-tracking branch 'origin/patch' 2025-08-19 14:56:22 -04:00
Ryan Kurtz
21b27795dd Merge remote-tracking branch 'origin/GP-5889_SwitchGuardDuplicate' into patch 2025-08-19 14:53:47 -04:00
Ryan Kurtz
a48c081e61 Merge remote-tracking branch
'origin/GP-5720-dragonmacher-drop-down-field-contains-mode--SQUASHED'
(Closes #4725, Closes #8203)
2025-08-19 13:01:58 -04:00
Ryan Kurtz
c7b125b1c7 Merge remote-tracking branch 'origin/GP-1-dragonmacher-tooltip-fix--SQUASHED' 2025-08-19 10:00:01 -04:00
Ryan Kurtz
c14f03c79a Merge remote-tracking branch 'origin/GP-5927_CondExecWithSubpiece'
(Closes #8022)
2025-08-19 09:56:01 -04:00
Ryan Kurtz
ed1a1b81f0 Merge remote-tracking branch
'origin/GP-3648-dragonmacher-fg-comparisons' (Closes #1154)
2025-08-19 09:53:50 -04:00
Ryan Kurtz
bcefa8bf09 Merge remote-tracking branch 'origin/patch' 2025-08-19 07:22:55 -04:00
Ryan Kurtz
105f9ef570 GP-5916: Checking for IMAGE_FUNCTION_RUNTIME_ENTRY.BeginAddress != 0
before creating function (#8414)
2025-08-19 07:19:10 -04:00
Ryan Kurtz
729642cbf6 Merge remote-tracking branch 'origin/GP-5912_emteere_SuperH_GBR' into
patch (Closes #4387)
2025-08-19 05:57:47 -04:00
Ryan Kurtz
be0ca420eb GP-5855: TLB prebuild fix 2025-08-19 05:55:23 -04:00
dragonmacher
3c90216365 GP-3648 - Add Function Graph to Function Comparison display 2025-08-18 17:14:26 -04:00
dragonmacher
ce0c7b9229 GP-5720 - Drop Down Modes - Added modes to drop-down text fields to control how matches are found 2025-08-18 16:39:20 -04:00
dragonmacher
b4f7c920e6 Fixed tooltip usage in memory search widget 2025-08-18 16:36:08 -04:00
Ryan Kurtz
6773801f6e Merge remote-tracking branch 'origin/GP-5581_sleighPiecewiseUniques--SQUASHED' 2025-08-18 13:44:28 -04:00
Ryan Kurtz
c001c4c65d Merge remote-tracking branch 'origin/patch' 2025-08-18 13:43:42 -04:00
Ryan Kurtz
f4ddff1a2c Merge remote-tracking branch 'origin/GP-5935_ryanmkurtz_pe' into patch
(Closes #8446)
2025-08-18 13:41:23 -04:00
Ryan Kurtz
328042f00f GP-5935: The IMAGE_RESOURCE_DIRECTORY_ENTRY data type is now correctly
defined as a structures instead of a union
2025-08-18 13:40:34 -04:00
Dan
42115f6df0 GP-5581: Allow optimization by truncation of subpieced unique varnodes. 2025-08-18 17:12:23 +00:00
Ryan Kurtz
6bc3871e67 Merge remote-tracking branch 'origin/GP-5923_dev747368_dynamiclabels_globalnamespace' 2025-08-18 12:50:21 -04:00
Ryan Kurtz
16a2e78806 Merge remote-tracking branch 'origin/GP-5914-dragonmacher-symbol-tree-nav-fix' 2025-08-18 12:47:50 -04:00
Ryan Kurtz
245ba82d8b Merge remote-tracking branch 'origin/GP-5793-dragonmacher-symbol-tree-external-path-update' 2025-08-18 12:45:22 -04:00
Ryan Kurtz
15ac693e76 Merge remote-tracking branch 'origin/GP-5855_ryanmkurtz_tlb--SQUASHED' 2025-08-18 10:54:45 -04:00
Ryan Kurtz
3ff52f05b1 GP-5855: Always use TLB prebuild 2025-08-18 10:51:56 -04:00
dragonmacher
42f4b3462e GP-5793 - Symbol Tree - Updated to respond to external program path
changes
2025-08-15 11:07:53 -04:00
dev747368
ad32d0177b GP-5923 don't default to Global namespace for dynamic symbols in columns
Tweak display of namespace columns to not default to Global namespace
for dynamic symbols.
2025-08-15 15:01:58 +00:00
dragonmacher
7b61358488 GP-5914 - Symbol Tree - Fixed issue that caused navigation when opening
a folder
2025-08-14 19:38:27 -04:00
caheckman
bb19782c35 GP-5927 Add support for SUBPIECE removal in ConditionalExecution 2025-08-13 23:22:35 +00:00
caheckman
9c9938e066 GP-5889 Check for common source in duplicated switch guard detection 2025-08-08 21:59:45 +00:00
emteere
28313c6574 GP-5912 Adding SH2 GBR register to the preserved by call list 2025-08-07 12:44:05 +00:00
242 changed files with 7766 additions and 2909 deletions

View file

@ -107,6 +107,9 @@ def isNativeBinaryMakeTask(Task task, String platform) {
*
******************************************************************************************/
def shouldSkipNative(task) {
if (rootProject.hasProperty("skipAllNatives")) {
return true;
}
return task.ext.has("skipNative") && task.ext.get("skipNative")
}

View file

@ -28,63 +28,56 @@ dependencies {
api project(':Debugger-rmi-trace')
}
ext.tlb = file("build/os/win_x86_64/dbgmodel.tlb")
def tlb = file("build/os/win_x86_64/dbgmodel.tlb")
def depsFile = file("${DEPS_DIR}/Debugger-agent-dbgeng/dbgmodel.tlb")
def binRepoFile = file("${BIN_REPO}/${getGhidraRelativePath(project)}/os/win_x86_64/dbgmodel.tlb")
if ("win_x86_64".equals(getCurrentPlatformName())) {
String makeName = "win_x86_64DbgmodelTlbMake"
task(type: Exec, makeName) {
ext.tmpBatch = file("build/buildTlb.bat")
ext.idl = file("src/main/py/src/ghidradbg/dbgmodel/DbgModel.idl")
inputs.file(idl)
outputs.file(tlb)
doFirst {
file(tlb).parentFile.mkdirs()
def midlCmd = "midl /tlb \"${tlb}\" \"${idl}\""
println "Executing: " + midlCmd
tmpBatch.withWriter { out ->
out.println "call " + VISUAL_STUDIO_VCVARS_CMD
out.println midlCmd
}
}
doLast {
assert file(tlb).exists() : "Failed to build dbgmodel.tlb"
}
commandLine "cmd", "/c", tmpBatch
}
tasks.assemblePyPackage {
from(tasks."$makeName") {
into("src/ghidradbg/dbgmodel/tlb")
}
}
}
else if (file(tlb).exists()) {
// required for multi-platform build
tasks.assemblePyPackage {
from(tlb) {
println "Copying existing tlb build artifact: " + tlb
into("src/ghidradbg/dbgmodel/tlb")
}
}
}
else {
def depsFile = file("${DEPS_DIR}/Debugger-agent-dbgeng/dbgmodel.tlb")
def binRepoFile = file("${BIN_REPO}/${getGhidraRelativePath(project)}/os/win_x86_64/dbgmodel.tlb")
tasks.assemblePyPackage {
def prebuiltTlb = depsFile.exists() ? depsFile : (binRepoFile.exists() ? binRepoFile : null)
assert prebuiltTlb != null : "Failed to locate prebuilt TLB file"
if (prebuiltTlb) {
tasks.assemblePyPackage {
from(prebuiltTlb) {
into("src/ghidradbg/dbgmodel/tlb")
}
from(prebuiltTlb) {
into("src/ghidradbg/dbgmodel/tlb")
}
}
task buildTlb(type: Exec) {
def tmpBatch = file("build/buildTlb.bat")
def idl = file("src/main/py/src/ghidradbg/dbgmodel/DbgModel.idl")
inputs.file(idl)
outputs.file(tlb)
doFirst {
assert isCurrentX86_64() && isCurrentWindows() : "Can only build TLB on Windows x86"
file(tlb).parentFile.mkdirs()
def midlCmd = "midl /tlb \"${tlb}\" \"${idl}\""
println "Executing: " + midlCmd
tmpBatch.withWriter { out ->
out.println "call " + VISUAL_STUDIO_VCVARS_CMD
out.println midlCmd
}
}
else {
println "****WARNING:**** dbgmodel.tlb omitted from ghidradbg python package"
doLast {
assert file(tlb).exists() : "Failed to build dbgmodel.tlb"
}
commandLine "cmd", "/c", tmpBatch
}
task prebuildTlb(type: Copy) {
dependsOn buildTlb
from tlb
into binRepoFile.parentFile
doFirst {
assert file(BIN_REPO).exists() : "Bin repo doesn't exist"
file(binRepoFile).parentFile.mkdirs()
}
}

View file

@ -3,13 +3,6 @@
##MODULE IP: MIT
Module.manifest||GHIDRA||||END|
README.md||GHIDRA||||END|
data/debugger-launchers/kernel-dbgeng.bat||GHIDRA||||END|
data/debugger-launchers/local-dbgeng-attach.bat||GHIDRA||||END|
data/debugger-launchers/local-dbgeng-ext.bat||GHIDRA||||END|
data/debugger-launchers/local-dbgeng-trace.bat||GHIDRA||||END|
data/debugger-launchers/local-dbgeng.bat||GHIDRA||||END|
data/debugger-launchers/remote-dbgeng.bat||GHIDRA||||END|
data/debugger-launchers/svrcx-dbgeng.bat||GHIDRA||||END|
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
src/main/help/help/topics/dbgeng/dbgeng.html||GHIDRA||||END|
src/main/py/LICENSE||GHIDRA||||END|

View file

@ -1,3 +1,18 @@
:: ###
:: 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.
:: ##
::@title dbgeng kernel
::@desc <html><body width="300px">
::@desc <h3>Kernel debugging using <tt>dbgeng</tt> (in a Python interpreter)</h3>

View file

@ -1,3 +1,18 @@
:: ###
:: 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.
:: ##
::@title dbgeng attach
::@desc <html><body width="300px">
::@desc <h3>Attach with <tt>dbgeng</tt> (in a Python interpreter)</h3>

View file

@ -1,3 +1,18 @@
:: ###
:: 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.
:: ##
::@title dbgeng extra options
::@image-opt env:OPT_TARGET_IMG
::@desc <html><body width="300px">

View file

@ -1,3 +1,18 @@
:: ###
:: 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.
:: ##
::@title dbgeng TTD
::@desc <html><body width="300px">
::@desc <h3>Open trace with <tt>dbgeng</tt> (in a Python interpreter)</h3>

View file

@ -1,3 +1,18 @@
:: ###
:: 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.
:: ##
::@title dbgeng
::@image-opt env:OPT_TARGET_IMG
::@desc <html><body width="300px">

View file

@ -1,3 +1,18 @@
:: ###
:: 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.
:: ##
::@title dbgeng remote
::@desc <html><body width="300px">
::@desc <h3>Connect to a remote debugger (via the .server interface)</h3>

View file

@ -1,3 +1,18 @@
:: ###
:: 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.
:: ##
::@title dbgeng svrcx
::@image-opt env:OPT_TARGET_IMG
::@desc <html><body width="300px">

View file

@ -2,16 +2,10 @@
##MODULE IP: JSch License
Module.manifest||GHIDRA||||END|
README.md||GHIDRA||||END|
data/debugger-launchers/local-gdb.ps1||GHIDRA||||END|
data/debugger-launchers/qemu-sys-gdb.ps1||GHIDRA||||END|
data/debugger-launchers/remote-gdb.ps1||GHIDRA||||END|
data/debugger-launchers/ssh-gdb.ps1||GHIDRA||||END|
data/debugger-launchers/ssh-gdbserver.ps1||GHIDRA||||END|
data/scripts/fallback_info_proc_mappings.gdb||GHIDRA||||END|
data/scripts/fallback_maintenance_info_sections.gdb||GHIDRA||||END|
data/scripts/getpid-linux-i386.gdb||GHIDRA||||END|
data/scripts/wine32_info_proc_mappings.gdb||GHIDRA||||END|
data/support/gdbsetuputils.ps1||GHIDRA||||END|
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
src/main/help/help/topics/gdb/gdb.html||GHIDRA||||END|
src/main/help/help/topics/gdb/images/GdbLauncher.png||GHIDRA||||END|

View file

@ -1,3 +1,18 @@
## ###
# 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.
##
#@title gdb
#@image-opt arg:1
#@desc <html><body width="300px">

View file

@ -1,3 +1,18 @@
## ###
# 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.
##
#@title gdb + qemu-system
#@image-opt env:OPT_TARGET_IMG
#@desc <html><body width="300px">

View file

@ -1,3 +1,18 @@
## ###
# 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.
##
#@title gdb remote
#@image-opt arg:1
#@desc <html><body width="300px">

View file

@ -1,3 +1,18 @@
## ###
# 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.
##
#@title gdb via ssh
#@image-opt arg:1
#@desc <html><body width="300px">

View file

@ -1,3 +1,18 @@
## ###
# 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.
##
#@title gdb + gdbserver via ssh
#@image-opt arg:1
#@desc <html><body width="300px">

View file

@ -1,4 +1,18 @@
## ###
# 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.
##
. $Env:MODULE_Debugger_rmi_trace_HOME\data\support\setuputils.ps1
function Add-Gdb-Init-Args {

View file

@ -4,12 +4,6 @@
Module.manifest||GHIDRA||||END|
README.md||GHIDRA||||END|
build.gradle||GHIDRA||||END|
data/debugger-launchers/android-lldb.ps1||GHIDRA||||END|
data/debugger-launchers/kernel-lldb.ps1||GHIDRA||||END|
data/debugger-launchers/local-lldb.ps1||GHIDRA||||END|
data/debugger-launchers/remote-lldb.ps1||GHIDRA||||END|
data/debugger-launchers/ssh-lldb.ps1||GHIDRA||||END|
data/support/lldbsetuputils.ps1||GHIDRA||||END|
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
src/main/help/help/topics/lldb/lldb.html||GHIDRA||||END|
src/main/py/LICENSE||GHIDRA||||END|

View file

@ -1,3 +1,18 @@
## ###
# 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.
##
#@title lldb Android
#@image-opt arg:1
#@desc <html><body width="300px">

View file

@ -1,3 +1,18 @@
## ###
# 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.
##
#@title lldb kernel (kdp)
#@desc <html><body width="300px">
#@desc <h3>Launch with local <tt>lldb</tt> and connect to a remote kernel</h3>

View file

@ -1,3 +1,18 @@
## ###
# 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.
##
#@title lldb
#@image-opt arg:1
#@desc <html><body width="300px">

View file

@ -1,3 +1,18 @@
## ###
# 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.
##
#@title lldb remote (gdb)
#@image-opt arg:1
#@desc <html><body width="300px">

View file

@ -1,3 +1,18 @@
## ###
# 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.
##
#@title lldb via ssh
#@image-opt arg:1
#@desc <html><body width="300px">

View file

@ -1,4 +1,18 @@
## ###
# 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.
##
. $Env:MODULE_Debugger_rmi_trace_HOME\data\support\setuputils.ps1
function Add-Lldb-Init-Args {

View file

@ -5,7 +5,6 @@ DEVNOTES.txt||GHIDRA||||END|
Module.manifest||GHIDRA||||END|
README.md||GHIDRA||||END|
data/ExtensionPoint.manifest||GHIDRA||||END|
data/support/setuputils.ps1||GHIDRA||||END|
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
src/main/help/help/topics/TraceRmiConnectionManagerPlugin/TraceRmiConnectionManagerPlugin.html||GHIDRA||||END|
src/main/help/help/topics/TraceRmiConnectionManagerPlugin/images/ConnectDialog.png||GHIDRA||||END|

View file

@ -1,3 +1,18 @@
## ###
# 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.
##
function Find-App-Properties {
[IO.FileInfo] $props = "$Env:GHIDRA_HOME\application.properties"
if ($props.Exists) {

View file

@ -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.
@ -30,8 +30,7 @@ import org.apache.commons.lang3.StringUtils;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.action.ToggleDockingAction;
import docking.action.ToolBarData;
import docking.action.*;
import docking.menu.ActionState;
import docking.menu.MultiStateDockingAction;
import docking.widgets.*;
@ -345,7 +344,8 @@ public class SampleGraphProvider extends ComponentProviderAdapter {
private void addLayoutAction() {
MultiStateDockingAction<LayoutProvider<SampleVertex, SampleEdge, SampleGraph>> layoutAction =
new MultiStateDockingAction<>(RELAYOUT_GRAPH_ACTION_NAME, plugin.getName()) {
new MultiStateDockingAction<>(RELAYOUT_GRAPH_ACTION_NAME, plugin.getName(),
KeyBindingType.SHARED) {
@Override
public void actionPerformed(ActionContext context) {

View file

@ -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,10 +25,10 @@ import ghidra.util.task.*;
/**
* Provides the ability to synchronize concurrent connection task
* instances within the same thread. This can occur within the swing thread due to the presence
* of a modal task dialog event queue. It also allows password cancelation to be propogated to the
* of a modal task dialog event queue. It also allows password cancellation to be propagated to the
* other tasks(s).
*/
public class BSimDBConnectTaskCoordinator {
public class BSimDBConnectTaskManager {
private final BSimServerInfo serverInfo;
@ -36,7 +36,7 @@ public class BSimDBConnectTaskCoordinator {
private boolean isCancelled = false;
private int count = 0;
public BSimDBConnectTaskCoordinator(BSimServerInfo serverInfo) {
public BSimDBConnectTaskManager(BSimServerInfo serverInfo) {
this.serverInfo = serverInfo;
}
@ -50,7 +50,7 @@ public class BSimDBConnectTaskCoordinator {
* Initiate a DB connection.
* @param connectionSupplier DB connection supplier
* @return DB connection
* @throws SQLException if a database connection error occured
* @throws SQLException if a database connection error occurred
* @throws CancelledSQLException if task was cancelled (password entry cancelled)
*/
public Connection getConnection(DBConnectionSupplier connectionSupplier) throws SQLException {
@ -66,7 +66,7 @@ public class BSimDBConnectTaskCoordinator {
.launchModal();
//@formatter:on
synchronized (BSimDBConnectTaskCoordinator.this) {
synchronized (BSimDBConnectTaskManager.this) {
Connection c = connectTask.getConnection();
if (c != null) {
return c;
@ -85,7 +85,7 @@ public class BSimDBConnectTaskCoordinator {
}
}
finally {
synchronized (BSimDBConnectTaskCoordinator.this) {
synchronized (BSimDBConnectTaskManager.this) {
if (--count == 0) {
clear();
}
@ -136,7 +136,7 @@ public class BSimDBConnectTaskCoordinator {
*/
@Override
public void run(TaskMonitor monitor) throws CancelledException {
synchronized (BSimDBConnectTaskCoordinator.this) {
synchronized (BSimDBConnectTaskManager.this) {
monitor.setMessage("Connecting...");
++count;
if (isCancelled) {

View file

@ -109,11 +109,11 @@ public class BSimPostgresDBConnectionManager {
private boolean successfulConnection = false;
private BasicDataSource bds = new BasicDataSource();
private BSimDBConnectTaskCoordinator taskCoordinator;
private BSimDBConnectTaskManager taskManager;
private BSimPostgresDataSource(BSimServerInfo serverInfo) {
this.serverInfo = serverInfo;
this.taskCoordinator = new BSimDBConnectTaskCoordinator(serverInfo);
this.taskManager = new BSimDBConnectTaskManager(serverInfo);
}
@Override
@ -254,7 +254,7 @@ public class BSimPostgresDBConnectionManager {
setDefaultProperties();
return taskCoordinator.getConnection(() -> connect());
return taskManager.getConnection(() -> connect());
}
@Override
@ -306,16 +306,18 @@ public class BSimPostgresDBConnectionManager {
loginError = "Access denied: " + serverInfo;
Msg.error(this, loginError);
}
// Use Ghidra's authentication infrastructure
connectionType = ConnectionType.SSL_Password_Authentication; // Try again with a password
// fallthru to second attempt at getConnection
// Try again with a password; fallthrough to second attempt at getConnection
connectionType = ConnectionType.SSL_Password_Authentication;
}
else if (e.getMessage().contains("SSL on") &&
e.getMessage().contains("no pg_hba.conf entry")) {
connectionType = ConnectionType.Unencrypted_No_Authentication; // Try again without any SSL
// Try again without any SSL; fallthrough to second attempt at getConnection
connectionType = ConnectionType.Unencrypted_No_Authentication;
bds.removeConnectionProperty("sslmode");
bds.removeConnectionProperty("sslfactory");
// fallthru to second attempt at getConnection
}
else {
throw e;
@ -364,6 +366,7 @@ public class BSimPostgresDBConnectionManager {
catch (SQLException e) {
if ((clientAuthenticator instanceof DefaultClientAuthenticator) &&
e.getMessage().contains("password authentication failed")) {
// wrong password provided via popup dialog - try again
loginError = "Access denied: " + serverInfo;
continue;

View file

@ -43,8 +43,8 @@ public class BSimH2FileDBConnectionManager {
private static boolean shutdownHookInstalled = false;
/**
* Get all H2 File DB data sorces which exist in the JVM.
* @return all H2 File DB data sorces
* Get all H2 File DB data sources which exist in the JVM.
* @return all H2 File DB data sources
*/
public static synchronized Collection<BSimH2FileDataSource> getAllDataSources() {
// Create copy to avoid potential concurrent modification
@ -138,11 +138,11 @@ public class BSimH2FileDBConnectionManager {
private boolean successfulConnection = false;
private BasicDataSource bds = new BasicDataSource();
private BSimDBConnectTaskCoordinator taskCoordinator;
private BSimDBConnectTaskManager taskManager;
private BSimH2FileDataSource(BSimServerInfo serverInfo) {
this.serverInfo = serverInfo;
this.taskCoordinator = new BSimDBConnectTaskCoordinator(serverInfo);
this.taskManager = new BSimDBConnectTaskManager(serverInfo);
}
@Override
@ -159,7 +159,7 @@ public class BSimH2FileDBConnectionManager {
* Delete the database files associated with this H2 File DB. This will fail immediately
* if active connections exist. Otherwise removal will be attempted and this data source
* will no longer be valid.
* @return true if DB sucessfully removed
* @return true if DB successfully removed
*/
public synchronized boolean delete() {
@ -278,7 +278,7 @@ public class BSimH2FileDBConnectionManager {
* Get a connection to the H2 file database.
* It is important to note that if the database does not exist and empty one will
* be created. The {@link #exists()} method should be used to check for the database
* existance prior to connecting the first time.
* existence prior to connecting the first time.
* @return database connection
* @throws SQLException if a database error occurs
*/
@ -294,7 +294,7 @@ public class BSimH2FileDBConnectionManager {
setDefaultProperties();
return taskCoordinator.getConnection(() -> connect());
return taskManager.getConnection(() -> connect());
}
@Override

View file

@ -317,6 +317,7 @@ src/main/help/help/topics/DataTypeEditors/images/BytesNumberInputDialog.png||GHI
src/main/help/help/topics/DataTypeEditors/images/Dialog.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeEditors/images/Dialog_Create_Pointer.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeEditors/images/Dialog_Multiple_Match.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeEditors/images/Dialog_SearchMode.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeEditors/images/Dialog_Select_Tree.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeEditors/images/Dialog_Single_Match.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeEditors/images/EnumEditor.png||GHIDRA||||END|

View file

@ -14,7 +14,7 @@ MachoRelocationHandler
ElfRelocationHandler
ElfExtension
RelocationFixupHandler
CodeComparisonPanel
CodeComparisonView
InstructionSkipper
DataTypeReferenceFinder
ChecksumAlgorithm

View file

@ -19,6 +19,59 @@
<P align="center"><IMG border="0" src="images/Dialog.png" alt=""><BR>
<I>Data Type Chooser Dialog</I></P>
<P>As you type text in the field, any potential matches will be displayed in the completion
window, which is described below.
</P>
<A NAME="SearchMode" />
<P>
The way matches are determined depends upon the search
mode you are in. The current mode is displayed at the right side of the text field,
indicated with a single character. Hovering over the character will show a tool tip
window that shows the name for the current mode.
</P>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P><IMG border="0" src="help/shared/tip.png" alt="">To change the search mode, click on
the seach mode character at the right side of the text field.
</P>
<P>
You can also change the search mode using <B>Ctrl Down</B> and <B>Ctrl Up</B> to
change the mode forward and backward, respectively.
</P>
<P align="center"><IMG border="0" src="images/Dialog_SearchMode.png" alt=""><BR>
<I>Data Type Chooser Dialog</I></P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<P>
By default, this chooser uses a <B>Starts With</B> matching mode. Any text typed will be
used to match all data type with a name that begins with the current search text.
</P>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P><IMG border="0" src="help/shared/tip.png" alt="">This data type selection chooser
performs the best with the 'starts with' setting. For a large number of data types,
this is the recommended search setting.
</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P><IMG border="0" src="help/shared/note.yellow.png" alt="">The text used to match is
based on the cursor position in the field. All text from the beginning up to the
cursor position will be used for the match. This allows you to arrow left and right
to control the matching list.
</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H2><A name="completion"></A> Completion Window</H2>
<BLOCKQUOTE>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -494,8 +494,10 @@
</P>
<H3><A name="Decompiler_Code_Comparison_Actions"></A>Decompiler Code Comparison Actions</H3>
<BLOCKQUOTE>
<H4><A name="Compare_Matching_Callees"></A>Compare Matching Callees</H4>
<BLOCKQUOTE>
<P> This action is available on matched tokens corresponding to function calls. It will open
@ -512,8 +514,31 @@
<P> This toggles whether or not constants must be exactly the same value to be a match in
the Decompiler Diff View. </P>
</BLOCKQUOTE>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H2><A name="FunctionGraph_Diff_View"></A>Function Graph Diff View</H2>
<BLOCKQUOTE>
<P>The <B><I> Function Graph Diff View</I></B> shows a pair of Function Graphs side by
side.
</P>
<BR><BR>
<H3><A name="FunctionGraph_Code_Comparison_Actions"></A>Function Graph Code Comparison Actions</H3>
<H4><A name="Dual_Function_Graph_View_Toggle_Orientation"></A>Show Function Graphs Side-by-Side</H4>
<BLOCKQUOTE>
<P> This toggles the Function Graph panels between a vertical split and a horizontal split. </P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H2><A name="Compare Multiple Functions"></A>Comparing Multiple Functions</H2>
<BLOCKQUOTE>

View file

@ -25,7 +25,7 @@ import docking.widgets.EmptyBorderButton;
import docking.widgets.TitledPanel;
import docking.widgets.button.GRadioButton;
import docking.widgets.fieldpanel.FieldPanel;
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import docking.widgets.fieldpanel.internal.FieldPanelScrollCoordinator;
import docking.widgets.label.GIconLabel;
import generic.theme.GIcon;
import ghidra.GhidraOptions;
@ -214,7 +214,7 @@ class ExternalAddConflictPanel extends JPanel implements CodeFormatService {
latestPanel.setProgram(latestProgram);
myPanel.setProgram(myProgram);
new FieldPanelCoordinator(
new FieldPanelScrollCoordinator(
new FieldPanel[] { latestPanel.getFieldPanel(), myPanel.getFieldPanel() });
buttonGroup = new ButtonGroup();

View file

@ -27,7 +27,7 @@ import docking.widgets.EmptyBorderButton;
import docking.widgets.TitledPanel;
import docking.widgets.checkbox.GCheckBox;
import docking.widgets.fieldpanel.FieldPanel;
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import docking.widgets.fieldpanel.internal.FieldPanelScrollCoordinator;
import docking.widgets.fieldpanel.support.BackgroundColorModel;
import generic.theme.GIcon;
import ghidra.GhidraOptions;
@ -66,7 +66,7 @@ public class ListingMergePanel extends JPanel
private JComponent bottomComp;
protected TitledPanel[] titlePanels;
private ListingPanel[] listingPanels;
private FieldPanelCoordinator coordinator;
private FieldPanelScrollCoordinator coordinator;
private FormatManager formatMgr;
private MultiListingLayoutModel multiModel;
private Program[] programs = new Program[4];
@ -120,7 +120,7 @@ public class ListingMergePanel extends JPanel
}
backgroundColorModel.addChangeListener(backgroundChangeListener);
coordinator = new FieldPanelCoordinator(fieldPanels);
coordinator = new FieldPanelScrollCoordinator(fieldPanels);
titlePanels[RESULT].addTitleComponent(new ShowHeaderButton());

View file

@ -130,9 +130,9 @@ public class CodeBrowserClipboardProvider extends ByteCopier
private String stringContent;
private boolean includeQuotesForStringData;
public CodeBrowserClipboardProvider(PluginTool tool, ComponentProvider codeViewerProvider) {
public CodeBrowserClipboardProvider(PluginTool tool, ComponentProvider componentProvider) {
this.tool = tool;
this.componentProvider = codeViewerProvider;
this.componentProvider = componentProvider;
PAINT_CONTEXT.setTextCopying(true);
@ -140,7 +140,6 @@ public class CodeBrowserClipboardProvider extends ByteCopier
includeQuotesForStringData =
!options.getBoolean(ClipboardPlugin.REMOVE_QUOTES_OPTION, false);
options.addOptionsChangeListener(this);
}
@Override

View file

@ -36,7 +36,7 @@ import docking.dnd.*;
import docking.widgets.EventTrigger;
import docking.widgets.fieldpanel.FieldPanel;
import docking.widgets.fieldpanel.HoverHandler;
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import docking.widgets.fieldpanel.internal.FieldPanelScrollCoordinator;
import docking.widgets.fieldpanel.support.*;
import docking.widgets.tab.GTabPanel;
import generic.theme.GIcon;
@ -105,7 +105,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
private ListingPanel otherPanel;
private CoordinatedListingPanelListener coordinatedListingPanelListener;
private FormatManager formatMgr;
private FieldPanelCoordinator coordinator;
private FieldPanelScrollCoordinator coordinator;
private ProgramSelectionListener liveProgramSelectionListener = (selection, trigger) -> {
liveSelection = selection;
updateSubTitle();
@ -753,7 +753,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
ListingModel otherAlignedModel = multiModel.getAlignedModel(1);
listingPanel.setListingModel(myAlignedModel);
lp.setListingModel(otherAlignedModel);
coordinator = new FieldPanelCoordinator(
coordinator = new FieldPanelScrollCoordinator(
new FieldPanel[] { listingPanel.getFieldPanel(), lp.getFieldPanel() });
addHoverServices(otherPanel);
HoverHandler hoverHandler = listingPanel.getFieldPanel().getHoverHandler();

View file

@ -18,8 +18,11 @@ package ghidra.app.plugin.core.function.editor;
import java.util.ArrayList;
import java.util.List;
import javax.help.UnsupportedOperationException;
import javax.swing.ListCellRenderer;
import org.apache.commons.lang3.StringUtils;
import docking.widgets.DropDownSelectionTextField;
import docking.widgets.DropDownTextFieldDataModel;
import docking.widgets.list.GListCellRenderer;
@ -37,6 +40,11 @@ public class RegisterDropDownSelectionDataModel implements DropDownTextFieldData
this.registers = registers;
}
@Override
public List<SearchMode> getSupportedSearchModes() {
return List.of(SearchMode.STARTS_WITH);
}
@Override
public ListCellRenderer<Register> getListRenderer() {
return new GListCellRenderer<Register>();
@ -54,11 +62,20 @@ public class RegisterDropDownSelectionDataModel implements DropDownTextFieldData
@Override
public List<Register> getMatchingData(String searchText) {
throw new UnsupportedOperationException(
"Method no longer supported. Instead, call getMatchingData(String, SearchMode)");
}
if (searchText == null || searchText.length() == 0) {
@Override
public List<Register> getMatchingData(String searchText, SearchMode searchMode) {
if (StringUtils.isBlank(searchText)) {
return registers;
}
if (searchMode != SearchMode.STARTS_WITH) {
throw new IllegalArgumentException("Unsupported SearchMode: " + searchMode);
}
searchText = searchText.toLowerCase();
List<Register> regList = new ArrayList<>();
@ -85,5 +102,4 @@ public class RegisterDropDownSelectionDataModel implements DropDownTextFieldData
}
return 0;
}
}

View file

@ -155,11 +155,18 @@ public class ExternalReferencesProvider extends ComponentProviderAdapter {
JPanel panel = new JPanel(new BorderLayout());
tableModel = new ExternalNamesTableModel();
table = new GhidraTable(tableModel);
table.getSelectionModel().addListSelectionListener(e -> {
if (e.getValueIsAdjusting()) {
return;
}
JScrollPane sp = new JScrollPane(table);
contextChanged();
});
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
ToolTipManager.sharedInstance().registerComponent(table);
JScrollPane sp = new JScrollPane(table);
panel.add(sp, BorderLayout.CENTER);
String namePrefix = "External Programs";

View file

@ -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,8 +16,6 @@
package ghidra.app.plugin.core.script;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.*;
import javax.swing.event.*;
@ -28,7 +26,6 @@ import docking.widgets.*;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.app.script.ScriptInfo;
import ghidra.util.HTMLUtilities;
import ghidra.util.UserSearchUtils;
/**
* A widget that allows the user to choose an existing script by typing its name or picking it
@ -222,24 +219,10 @@ public class ScriptSelectionEditor {
}
@Override
public List<ScriptInfo> getMatchingData(String searchText) {
// This pattern will: 1) allow users to match the typed text anywhere in the
// script names and 2) allow the use of globbing characters
Pattern pattern = UserSearchUtils.createContainsPattern(searchText, true,
Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
List<ScriptInfo> results = new ArrayList<>();
for (ScriptInfo info : data) {
String name = info.getName();
Matcher m = pattern.matcher(name);
if (m.matches()) {
results.add(info);
}
}
return results;
public List<SearchMode> getSupportedSearchModes() {
return List.of(SearchMode.CONTAINS, SearchMode.WILDCARD, SearchMode.STARTS_WITH);
}
}
private class ScriptSelectionTextField extends DropDownSelectionTextField<ScriptInfo> {

View file

@ -19,6 +19,7 @@ import static ghidra.framework.model.DomainObjectEvent.*;
import static ghidra.program.util.ProgramEvent.*;
import java.awt.BorderLayout;
import java.awt.Point;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.event.MouseAdapter;
@ -167,7 +168,8 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
return;
}
maybeGoToSymbol();
SymbolNode symbolNode = getSelectedSymbolNode();
maybeGoToSymbol(symbolNode);
contextChanged();
});
@ -175,14 +177,24 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
@Override
public void mouseClicked(MouseEvent e) {
// This code serves to perform navigation in the case that the selection handler
// above does not, as is the case when the node is already selected. This code
// will get called on the mouse release, whereas the selection handler gets called
// on the mouse pressed.
// This code serves to perform navigation in the case that the selection handler
// above does not, as is the case when the clicked node is already selected. This
// code will get called on the mouse clicked, whereas the selection handler gets
// called on the mouse pressed.
// For now, just attempt to perform the goto. It may get called twice, but this
// should have no real impact on performance.
Point p = e.getPoint();
GTreeNode clickedNode = newTree.getNodeForLocation(p.x, p.y);
if (clickedNode == null) {
return;
}
maybeGoToSymbol();
SymbolNode symbolNode = getSelectedSymbolNode();
if (!clickedNode.equals(symbolNode)) {
return;
}
maybeGoToSymbol(symbolNode);
}
});
@ -211,19 +223,24 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
}
}
private void maybeGoToSymbol() {
private SymbolNode getSelectedSymbolNode() {
TreePath[] paths = tree.getSelectionPaths();
if (paths == null || paths.length != 1) {
return;
return null;
}
Object object = paths[0].getLastPathComponent();
if (!(object instanceof SymbolNode)) {
return;
if (object instanceof SymbolNode symbolNode) {
return symbolNode;
}
SymbolNode node = (SymbolNode) object;
return null;
}
private void maybeGoToSymbol(SymbolNode node) {
if (node == null) {
return;
}
Symbol symbol = node.getSymbol();
SymbolType type = symbol.getSymbolType();
if (!type.isNamespace() || type == SymbolType.FUNCTION) {
@ -613,6 +630,11 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
.each(SYMBOL_REMOVED).call(this::processSymbolRemoved)
.each(EXTERNAL_ENTRY_ADDED, EXTERNAL_ENTRY_REMOVED)
.call(this::processExternalEntryChanged)
.any(EXTERNAL_PATH_CHANGED, EXTERNAL_NAME_ADDED,
EXTERNAL_NAME_REMOVED, EXTERNAL_NAME_CHANGED)
// Rather than try to find each affected symbol, it is easier to just reload
// the tree. This is infrequent enough that it should not be disruptive.
.call(this::reloadTree)
// handle function changes specially so that we can perform coalesce changes
.any(FUNCTION_CHANGED).call(this::processAllFunction)

View file

@ -68,13 +68,18 @@ public abstract class SymbolCategoryNode extends SymbolTreeNode {
@Override
public List<GTreeNode> generateChildren(TaskMonitor monitor) throws CancelledException {
if (!isEnabled) {
return Collections.emptyList();
return List.of();
}
SymbolTreeRootNode root = (SymbolTreeRootNode) getRoot();
if (root == null) {
// this can happen if the tree is reloaded while we are searching in a background task
return List.of();
}
SymbolType symbolType = symbolCategory.getSymbolType();
List<GTreeNode> list = getSymbols(symbolType, monitor);
monitor.checkCancelled();
SymbolTreeRootNode root = (SymbolTreeRootNode) getRoot();
int groupThreshold = root.getNodeGroupThreshold();
return OrganizationNode.organize(list, groupThreshold, monitor);
}
@ -322,6 +327,9 @@ public abstract class SymbolCategoryNode extends SymbolTreeNode {
if (this == o) {
return true;
}
if (o == null) {
return false;
}
if (getClass() != o.getClass()) {
return false;
}

View file

@ -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.
@ -482,7 +482,7 @@ public abstract class AbstractSymbolTableModel extends AddressBasedTableModel<Sy
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
ServiceProvider svcProvider) throws IllegalArgumentException {
Symbol symbol = rowObject.getSymbol();
if (symbol == null || symbol.isDeleted()) {
if (symbol == null || symbol.isDeleted() || symbol.isDynamic()) {
return null;
}
return symbol.getParentNamespace().getName(true);

View file

@ -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.
@ -19,6 +19,7 @@ import java.util.Collection;
import ghidra.features.base.codecompare.model.FunctionComparisonModel;
import ghidra.features.base.codecompare.model.MatchedFunctionComparisonModel;
import ghidra.features.base.codecompare.panel.FunctionComparisonPanel;
import ghidra.program.model.listing.Function;
import utility.function.Callback;
@ -84,6 +85,17 @@ public interface FunctionComparisonService {
* @param closeListener an optional callback if the client wants to be notified when the
* associated function comparison windows is closed.
*/
public void createCustomComparison(FunctionComparisonModel model,
Callback closeListener);
public void createCustomComparison(FunctionComparisonModel model, Callback closeListener);
/**
* Creates a new comparison view that the caller can install into their UI. This is in contrast
* with {@link #createCustomComparison(FunctionComparisonModel, Callback)}, which will install
* the new comparison into an existing UI.
* <p>
* Note: clients are responsible for calling {@link FunctionComparisonPanel#dispose()} when done
* using the panel.
*
* @return the new panel
*/
public FunctionComparisonPanel createComparisonViewer();
}

View file

@ -144,7 +144,7 @@ public class ImageRuntimeFunctionEntries_X86 implements ImageRuntimeFunctionEntr
*/
public void markup(Program program) throws DuplicateNameException, IOException {
if (!unwindInfo.hasChainedUnwindInfo()) {
if (beginAddress != 0 && !unwindInfo.hasChainedUnwindInfo()) {
AbstractProgramLoader.markAsFunction(program, null,
program.getImageBase().add(beginAddress));
}

View file

@ -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.
@ -161,8 +161,7 @@ public class ResourceDirectoryEntry implements StructConverter {
}
/**
* Returns true if the parent resource directory is named,
* false indicates an ID.
* {@return true if the parent resource directory is named; false indicates an ID}
*/
public boolean isNameEntry() {
return isNameEntry;
@ -254,8 +253,14 @@ public class ResourceDirectoryEntry implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType nameStruct = new StructureDataType(NAME + "_" + "NameStruct", 0);
nameStruct.add(DWORD, "NameOffset", null);
nameStruct.add(DWORD, "NameIsString", null);
nameStruct.setPackingEnabled(true);
try {
nameStruct.addBitField(DWORD, 31, "NameOffset", null);
nameStruct.addBitField(DWORD, 1, "NameIsString", null);
}
catch (InvalidDataTypeException e) {
throw new IOException(e);
}
nameStruct.setCategoryPath(new CategoryPath("/PE"));
UnionDataType union1 = new UnionDataType(NAME + "_" + "NameUnion");
@ -265,19 +270,25 @@ public class ResourceDirectoryEntry implements StructConverter {
union1.setCategoryPath(new CategoryPath("/PE"));
StructureDataType offsetStruct = new StructureDataType(NAME + "_" + "DirectoryStruct", 0);
offsetStruct.add(DWORD, "OffsetToDirectory", null);
offsetStruct.add(DWORD, "DataIsDirectory", null);
offsetStruct.setPackingEnabled(true);
try {
offsetStruct.addBitField(DWORD, 31, "OffsetToDirectory", null);
offsetStruct.addBitField(DWORD, 1, "DataIsDirectory", null);
}
catch (InvalidDataTypeException e) {
throw new IOException(e);
}
UnionDataType union2 = new UnionDataType(NAME + "_" + "DirectoryUnion");
union2.add(DWORD, "OffsetToData", null);
union2.add(offsetStruct, offsetStruct.getName(), null);
UnionDataType union3 = new UnionDataType(NAME);
union3.add(union1, "NameUnion", null);
union3.add(union2, "DirectoryUnion", null);
StructureDataType struct = new StructureDataType(NAME, 0);
struct.add(union1, "NameUnion", null);
struct.add(union2, "DirectoryUnion", null);
union3.setCategoryPath(new CategoryPath("/PE"));
return union3;
struct.setCategoryPath(new CategoryPath("/PE"));
return struct;
}
public boolean isValid() {

View file

@ -18,14 +18,18 @@ package ghidra.app.util.datatype;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.help.UnsupportedOperationException;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.*;
import javax.swing.tree.TreePath;
import org.apache.commons.lang3.StringUtils;
import docking.widgets.DropDownSelectionTextField;
import docking.widgets.DropDownTextFieldDataModel;
import docking.widgets.button.BrowseButton;
@ -406,16 +410,37 @@ public class CategoryPathSelectionEditor extends AbstractCellEditor {
return categoryPath.getPath();
}
@Override
public List<SearchMode> getSupportedSearchModes() {
return List.of(SearchMode.CONTAINS, SearchMode.STARTS_WITH, SearchMode.WILDCARD);
}
@Override
public List<CategoryPath> getMatchingData(String searchText) {
if (searchText == null || searchText.length() == 0) {
return Collections.emptyList();
throw new UnsupportedOperationException(
"Method no longer supported. Instead, call getMatchingData(String, SearchMode)");
}
@Override
public List<CategoryPath> getMatchingData(String searchText, SearchMode mode) {
if (StringUtils.isBlank(searchText)) {
return new ArrayList<>(data);
}
if (!getSupportedSearchModes().contains(mode)) {
throw new IllegalArgumentException("Unsupported SearchMode: " + mode);
}
Pattern p = mode.createPattern(searchText);
return getMatchingDataRegex(p);
}
private List<CategoryPath> getMatchingDataRegex(Pattern p) {
List<CategoryPath> results = new ArrayList<>();
for (CategoryPath path : data) {
String pathString = path.getPath();
if (pathString.contains(searchText)) {
Matcher m = p.matcher(pathString);
if (m.matches()) {
results.add(path);
}
}

View file

@ -17,7 +17,10 @@ package ghidra.app.util.datatype;
import java.awt.Component;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.help.UnsupportedOperationException;
import javax.swing.*;
import docking.widgets.DropDownSelectionTextField;
@ -69,6 +72,11 @@ public class DataTypeDropDownSelectionDataModel implements DropDownTextFieldData
return service;
}
@Override
public List<SearchMode> getSupportedSearchModes() {
return List.of(SearchMode.STARTS_WITH, SearchMode.CONTAINS, SearchMode.WILDCARD);
}
@Override
public ListCellRenderer<DataType> getListRenderer() {
return new DataTypeDropDownRenderer();
@ -86,13 +94,47 @@ public class DataTypeDropDownSelectionDataModel implements DropDownTextFieldData
@Override
public List<DataType> getMatchingData(String searchText) {
throw new UnsupportedOperationException(
"Method no longer supported. Instead, call getMatchingData(String, SearchMode)");
}
@Override
public List<DataType> getMatchingData(String searchText, SearchMode mode) {
if (searchText == null || searchText.length() == 0) {
// full list results not supported since the data may be too large for user interaction
return Collections.emptyList();
}
List<DataType> dataTypeList =
if (!getSupportedSearchModes().contains(mode)) {
throw new IllegalArgumentException("Unsupported SearchMode: " + mode);
}
if (mode == SearchMode.STARTS_WITH) {
return getMatchDataStartsWith(searchText);
}
Pattern p = mode.createPattern(searchText);
return getMatchingDataRegex(p);
}
private List<DataType> getMatchDataStartsWith(String searchText) {
List<DataType> results =
DataTypeUtils.getStartsWithMatchingDataTypes(searchText, dataTypeService);
return filterDataTypeList(dataTypeList);
return filterDataTypeList(results);
}
private List<DataType> getMatchingDataRegex(Pattern p) {
List<DataType> results = new ArrayList<>();
List<DataType> allTypes = dataTypeService.getSortedDataTypeList();
for (DataType dt : allTypes) {
String name = dt.getName().toLowerCase();
Matcher m = p.matcher(name);
if (m.matches()) {
results.add(dt);
}
}
return filterDataTypeList(results);
}
/**

View file

@ -151,6 +151,8 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
editorPanel.add(selectionField);
editorPanel.add(browsePanel);
// This listener is not installed under certain conditions, such as when
// setTabCommitsEdit(true) is called.
keyListener = new KeyAdapter() {
@Override

View file

@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.List;
import ghidra.app.nav.Navigatable;
import ghidra.app.nav.NavigationUtils;
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
import ghidra.app.services.GoToService;
import ghidra.app.services.ProgramManager;
@ -54,10 +53,11 @@ public class MnemonicFieldMouseHandler implements FieldMouseHandlerExtension {
Program program = programManager.getCurrentProgram();
Listing listing = program.getListing();
CodeUnit codeUnit = listing.getCodeUnitAt(location.getAddress());
return checkMemReferences(codeUnit, serviceProvider);
return checkMemReferences(codeUnit, sourceNavigatable, serviceProvider);
}
private boolean checkMemReferences(CodeUnit codeUnit, ServiceProvider serviceProvider) {
private boolean checkMemReferences(CodeUnit codeUnit, Navigatable navigatable,
ServiceProvider serviceProvider) {
if (codeUnit == null) {
return false;
@ -77,8 +77,8 @@ public class MnemonicFieldMouseHandler implements FieldMouseHandlerExtension {
TableService service = serviceProvider.getService(TableService.class);
if (service != null) {
Navigatable nav = NavigationUtils.getActiveNavigatable();
service.showTable("Mnemonic References", "Mnemonic", model, "References", nav);
service.showTable("Mnemonic References", "Mnemonic", model, "References",
navigatable);
return true;
}
}
@ -96,7 +96,7 @@ public class MnemonicFieldMouseHandler implements FieldMouseHandlerExtension {
GoToService goToService = serviceProvider.getService(GoToService.class);
if (goToService != null) {
return goToService.goTo(loc);
return goToService.goTo(navigatable, loc, navigatable.getProgram());
}
}

View file

@ -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.
@ -83,9 +83,15 @@ public class ProgramLocationTranslator {
}
}
// Adjust symbol path for labels if it is part of the location.
adjustSymbolPath(saveState, otherSideAddress, address, byteAddress, desiredByteAddress,
otherSideLocation.getProgram(), program);
String[] symbolPathArray = saveState.getStrings("_SYMBOL_PATH", new String[0]);
if (symbolPathArray.length != 0) {
// Adjust symbol path for labels if it is part of the location.
boolean hasSymbol = adjustSymbolPath(saveState, otherSideAddress, address, byteAddress,
desiredByteAddress, otherSideLocation.getProgram(), program);
if (!hasSymbol) {
return new ProgramLocation(program, desiredByteAddress);
}
}
// ref address can't be used with indicated side so remove it.
saveState.remove("_REF_ADDRESS");
@ -186,32 +192,28 @@ public class ProgramLocationTranslator {
return ProgramLocation.getLocation(correlator.getProgram(side), saveState);
}
private void adjustSymbolPath(SaveState saveState, Address address, Address desiredAddress,
private boolean adjustSymbolPath(SaveState saveState, Address address, Address desiredAddress,
Address byteAddress, Address desiredByteAddress, Program program,
Program desiredProgram) {
String[] symbolPathArray = saveState.getStrings("_SYMBOL_PATH", new String[0]);
saveState.remove("_SYMBOL_PATH");
if (symbolPathArray.length == 0) {
return; // save state has no labels for program location.
}
Address symbolAddress = (byteAddress != null) ? byteAddress : address;
Address desiredSymbolAddress =
(desiredByteAddress != null) ? desiredByteAddress : desiredAddress;
if (symbolAddress == null || desiredSymbolAddress == null) {
return; // no address match.
return false; // no address match.
}
Symbol[] symbols = program.getSymbolTable().getSymbols(symbolAddress);
if (symbols.length == 0) {
return; // no symbols in program for matching.
return false; // no symbols in program for matching.
}
Symbol[] desiredSymbols = desiredProgram.getSymbolTable().getSymbols(desiredSymbolAddress);
if (desiredSymbols.length == 0) {
return; // no symbols in desiredProgram for matching.
return false; // no symbols in desiredProgram for matching.
}
int desiredRow = adjustSymbolRow(saveState, symbols, desiredSymbols);
int desiredIndex = getDesiredSymbolIndex(desiredSymbols, desiredRow);
// Now get the desired symbol.
@ -219,6 +221,7 @@ public class ProgramLocationTranslator {
SymbolPath symbolPath = getSymbolPath(desiredSymbol);
// Set symbol path for desiredProgram in the save state.
saveState.putStrings("_SYMBOL_PATH", symbolPath.asArray());
return true;
}
private int adjustSymbolRow(SaveState saveState, Symbol[] symbols, Symbol[] desiredSymbols) {

View file

@ -37,8 +37,8 @@ import ghidra.app.plugin.core.functioncompare.actions.*;
import ghidra.app.util.ListingHighlightProvider;
import ghidra.app.util.viewer.format.*;
import ghidra.app.util.viewer.listingpanel.*;
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
import ghidra.features.base.codecompare.panel.CodeComparisonPanelActionContext;
import ghidra.features.base.codecompare.panel.CodeComparisonViewActionContext;
import ghidra.features.base.codecompare.panel.CodeComparisonView;
import ghidra.framework.options.*;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
@ -57,12 +57,10 @@ import ghidra.util.task.TaskMonitor;
import help.Help;
/**
* Panel that displays two listings for comparison.
* UI that displays two listings for comparison.
*/
public class ListingCodeComparisonPanel
extends CodeComparisonPanel implements
FormatModelListener, OptionsChangeListener {
public class ListingCodeComparisonView
extends CodeComparisonView implements FormatModelListener, OptionsChangeListener {
public static final String NAME = "Listing View";
private static final String DIFF_NAVIGATE_GROUP = "A2_DiffNavigate";
@ -87,7 +85,7 @@ public class ListingCodeComparisonPanel
private ListingAddressCorrelation addressCorrelator;
private ListingDiff listingDiff;
private ListingCoordinator coordinator;
private ListingDisplaySynchronizer coordinator;
private boolean listingsLocked;
private ListingDiffActionManager diffActionManager;
@ -107,7 +105,7 @@ public class ListingCodeComparisonPanel
* @param owner the owner of this panel
* @param tool the tool displaying this panel
*/
public ListingCodeComparisonPanel(String owner, PluginTool tool) {
public ListingCodeComparisonView(String owner, PluginTool tool) {
super(owner, tool);
Help.getHelpService().registerHelp(this, new HelpLocation(HELP_TOPIC, "Listing_View"));
initializeOptions();
@ -450,7 +448,7 @@ public class ListingCodeComparisonPanel
.description("Show the tool options for the Listing Code Comparison.")
.popupMenuPath("Properties")
.helpLocation(new HelpLocation(HELP_TOPIC, "Listing_Code_Comparison_Options"))
.validContextWhen(c -> isValidPanelContext(c))
.validWhen(c -> isValidPanelContext(c))
.enabledWhen(c -> isShowing() && listingDiff.hasCorrelation())
.onAction(c -> showOptionsDialog())
.build();
@ -497,10 +495,10 @@ public class ListingCodeComparisonPanel
}
private boolean isValidPanelContext(ActionContext context) {
if (!(context instanceof CodeComparisonPanelActionContext comparisonContext)) {
if (!(context instanceof CodeComparisonViewActionContext comparisonContext)) {
return false;
}
CodeComparisonPanel comparisonPanel = comparisonContext.getCodeComparisonPanel();
CodeComparisonView comparisonPanel = comparisonContext.getCodeComparisonView();
return comparisonPanel == this;
}
@ -521,7 +519,7 @@ public class ListingCodeComparisonPanel
coordinator = null;
}
if (listingsLocked) {
coordinator = new ListingCoordinator(displays, addressCorrelator);
coordinator = new ListingDisplaySynchronizer(displays, addressCorrelator);
coordinator.sync(activeSide);
}
}

View file

@ -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,24 +23,24 @@ import ghidra.features.base.codecompare.panel.CodeComparisonActionContext;
*/
public class ListingComparisonActionContext extends CodeComparisonActionContext {
private ListingCodeComparisonPanel codeComparisonPanel = null;
private ListingCodeComparisonView codeComparisonPanel = null;
/**
* Constructor for a dual listing's action context.
* @param provider the provider that uses this action context.
* @param panel the ListingCodeComparisonPanel that generated this context
*/
public ListingComparisonActionContext(ComponentProvider provider, ListingCodeComparisonPanel panel) {
public ListingComparisonActionContext(ComponentProvider provider, ListingCodeComparisonView panel) {
super(provider, panel, panel.getActiveListingPanel().getFieldPanel());
this.codeComparisonPanel = panel;
}
/**
* Returns the {@link ListingCodeComparisonPanel} that generated this context
* Returns the {@link ListingCodeComparisonView} that generated this context
* @return the listing comparison panel that generated this context
*/
@Override
public ListingCodeComparisonPanel getCodeComparisonPanel() {
public ListingCodeComparisonView getCodeComparisonView() {
return codeComparisonPanel;
}

View file

@ -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.
@ -151,7 +151,7 @@ public class ListingDiffActionManager {
ToggleIgnoreRegisterNamesAction() {
super("Toggle Ignore Register Names", "DualListing");
setDescription(HTMLUtilities.toHTML(
"If selected, difference highlights should\n" + "ignore operand Registers."));
"If selected, difference highlights should\nignore operand Registers."));
setEnabled(true);
setPopupMenuData(new MenuData(
new String[] { "Ignore Operand Registers As Differences" },

View file

@ -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.
@ -20,8 +20,8 @@ import static ghidra.util.datastruct.Duo.Side.*;
import java.math.BigInteger;
import docking.widgets.fieldpanel.FieldPanel;
import docking.widgets.fieldpanel.internal.LayoutLockedFieldPanelCoordinator;
import docking.widgets.fieldpanel.internal.LineLockedFieldPanelCoordinator;
import docking.widgets.fieldpanel.internal.LayoutLockedFieldPanelScrollCoordinator;
import docking.widgets.fieldpanel.internal.LineLockedFieldPanelScrollCoordinator;
import docking.widgets.fieldpanel.support.ViewerPosition;
import ghidra.app.util.viewer.listingpanel.ProgramLocationTranslator;
import ghidra.app.util.viewer.util.AddressIndexMap;
@ -34,23 +34,23 @@ import ghidra.util.datastruct.Duo.Side;
/**
* Keeps two listing panels synchronized, both the view and cursor location
*/
public class ListingCoordinator {
class ListingDisplaySynchronizer {
private Duo<ListingDisplay> displays;
private Duo<Address> lockLineAddresses = new Duo<>();
private ProgramLocationTranslator locationTranslator;
private LineLockedFieldPanelCoordinator viewCoordinator;
private LineLockedFieldPanelScrollCoordinator viewCoordinator;
ListingCoordinator(Duo<ListingDisplay> displays, ListingAddressCorrelation correlator) {
ListingDisplaySynchronizer(Duo<ListingDisplay> displays,
ListingAddressCorrelation correlation) {
this.displays = displays;
this.locationTranslator = new ProgramLocationTranslator(correlator);
this.locationTranslator = new ProgramLocationTranslator(correlation);
FieldPanel left = displays.get(LEFT).getListingPanel().getFieldPanel();
FieldPanel right = displays.get(RIGHT).getListingPanel().getFieldPanel();
viewCoordinator = new LayoutLockedFieldPanelCoordinator(left, right);
viewCoordinator = new LayoutLockedFieldPanelScrollCoordinator(left, right);
}
/**
* notification that the given side change to the given location
* Notification that the given side change to the given location
* @param side the side that changed
* @param location the location from the given side
*/
@ -63,9 +63,7 @@ public class ListingCoordinator {
if (otherLocation != null) {
updateViewCoordinator(side, location, otherLocation);
displays.get(otherSide).goTo(otherLocation);
displays.get(side.otherSide()).updateCursorMarkers(otherLocation);
}
}
void dispose() {
@ -73,7 +71,7 @@ public class ListingCoordinator {
}
/**
* synchronized the two listings using the given side as the source
* Synchronize the two listings using the given side as the source
* @param side to synchronize from
*/
void sync(Side side) {
@ -109,7 +107,7 @@ public class ListingCoordinator {
if (leftAddress == null || rightAddress == null) {
return;
}
lockLineAddresses = new Duo<>(leftAddress, rightAddress);
AddressIndexMap leftMap = displays.get(LEFT).getListingPanel().getAddressIndexMap();
AddressIndexMap rightMap = displays.get(RIGHT).getListingPanel().getAddressIndexMap();

View file

@ -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.
@ -46,7 +46,7 @@ abstract class ListingDisplayToggleAction extends ToggleDockingAction {
@Override
public boolean isAddToPopup(ActionContext context) {
Object contextObject = context.getContextObject();
if (contextObject instanceof ListingCodeComparisonPanel) {
if (contextObject instanceof ListingCodeComparisonView) {
Object sourceObject = context.getSourceObject();
return sourceObject instanceof FieldPanel;
}

View file

@ -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,19 +23,20 @@ import ghidra.program.model.listing.Function;
import ghidra.util.datastruct.Duo.Side;
public abstract class CodeComparisonActionContext extends DefaultActionContext
implements CodeComparisonPanelActionContext {
private CodeComparisonPanel comparisonPanel;
implements CodeComparisonViewActionContext {
private CodeComparisonView comparisonProvider;
/**
* Constructor
* @param provider the ComponentProvider containing the code comparison panel
* @param panel the CodeComparisonPanel that generated this context
* @param comparisonProvider the provider that generated this context
* @param component the focusable component for associated with the comparison panel
*/
public CodeComparisonActionContext(ComponentProvider provider, CodeComparisonPanel panel,
public CodeComparisonActionContext(ComponentProvider provider,
CodeComparisonView comparisonProvider,
Component component) {
super(provider, panel, component);
this.comparisonPanel = panel;
super(provider, comparisonProvider, component);
this.comparisonProvider = comparisonProvider;
}
/**
@ -44,8 +45,8 @@ public abstract class CodeComparisonActionContext extends DefaultActionContext
* @return the function to get information from
*/
public Function getSourceFunction() {
Side activeSide = comparisonPanel.getActiveSide();
return comparisonPanel.getFunction(activeSide.otherSide());
Side activeSide = comparisonProvider.getActiveSide();
return comparisonProvider.getFunction(activeSide.otherSide());
}
/**
@ -54,7 +55,7 @@ public abstract class CodeComparisonActionContext extends DefaultActionContext
* @return the function to apply information to
*/
public Function getTargetFunction() {
Side activeSide = comparisonPanel.getActiveSide();
return comparisonPanel.getFunction(activeSide);
Side activeSide = comparisonProvider.getActiveSide();
return comparisonProvider.getFunction(activeSide);
}
}

View file

@ -31,6 +31,7 @@ import docking.ComponentProvider;
import docking.action.*;
import docking.widgets.TitledPanel;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Function;
@ -41,17 +42,19 @@ import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.classfinder.ExtensionPoint;
import ghidra.util.datastruct.Duo;
import ghidra.util.datastruct.Duo.Side;
import utility.function.Callback;
/**
* The CodeComparisonPanel class should be extended by any class that is to be
* The {@link CodeComparisonView} class should be extended by any class that is to be
* discovered by the {@link FunctionComparisonPanel} class and included as a
* form of comparing two sections of code within the same or different programs
* <p>
* NOTE: ALL CodeComparisonPanel CLASSES MUST END IN
* <code>CodeComparisonPanel</code> so they are discoverable by the {@link ClassSearcher}
* NOTE: ALL CodeComparisonView CLASSES MUST END IN
* <code>CodeComparisonView</code> so they are discoverable by the {@link ClassSearcher}
*/
public abstract class CodeComparisonPanel extends JPanel
public abstract class CodeComparisonView extends JPanel
implements ExtensionPoint {
public static final String HELP_TOPIC = "FunctionComparison";
private static final Color ACTIVE_BORDER_COLOR = Palette.getColor("lightpink");
private static final int MINIMUM_PANEL_WIDTH = 50;
@ -71,6 +74,7 @@ public abstract class CodeComparisonPanel extends JPanel
private ToggleOrientationAction toggleOrientationAction;
private JComponent northComponent;
private boolean showTitles = true;
private Callback orientationChangedCallback = Callback.dummy();
/**
* Constructor
@ -78,7 +82,7 @@ public abstract class CodeComparisonPanel extends JPanel
* @param owner the name of the owner of this component
* @param tool the tool that contains the component
*/
protected CodeComparisonPanel(String owner, PluginTool tool) {
protected CodeComparisonView(String owner, PluginTool tool) {
this.owner = owner;
this.tool = tool;
toggleOrientationAction = new ToggleOrientationAction(getName());
@ -91,6 +95,10 @@ public abstract class CodeComparisonPanel extends JPanel
return tool;
}
public void setSaveState(SaveState saveState) {
// for subclasses
}
/**
* Displays a comparison of two ComparisonData objects
*
@ -163,7 +171,7 @@ public abstract class CodeComparisonPanel extends JPanel
public abstract void dispose();
/**
* Returns the context object which corresponds to the area of focus within this provider's
* Returns the context object which corresponds to the area of focus within this view's
* component. Null is returned when there is no context.
* @param componentProvider the provider that includes this code comparison component.
* @param event mouse event which corresponds to this request.
@ -296,6 +304,12 @@ public abstract class CodeComparisonPanel extends JPanel
: JSplitPane.VERTICAL_SPLIT;
splitPane.setOrientation(orientation);
splitPane.setDividerLocation(0.5);
orientationChangedCallback.call();
}
public void setOrientationChangedCallback(Callback callback) {
this.orientationChangedCallback = Callback.dummyIfNull(callback);
}
private void setTitle(TitledPanel titlePanel, String titlePrefix, String title) {
@ -343,7 +357,7 @@ public abstract class CodeComparisonPanel extends JPanel
setActiveSide(LEFT);
}
private void addMouseAndFocusListeners(Side side) {
protected void addMouseAndFocusListeners(Side side) {
JComponent comp = getComparisonComponent(side);
comp.addFocusListener(new FocusAdapter() {
@Override
@ -374,6 +388,7 @@ public abstract class CodeComparisonPanel extends JPanel
}
private void addMouseListenerRecursively(Component component, MouseListener listener) {
component.removeMouseListener(listener);
component.addMouseListener(listener);
if (component instanceof Container container) {
for (int i = 0; i < container.getComponentCount(); i++) {

View file

@ -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,14 +16,14 @@
package ghidra.features.base.codecompare.panel;
/**
* Action context for a CodeComparisonPanel.
* Action context for a {@link CodeComparisonView}.
*/
public interface CodeComparisonPanelActionContext {
public interface CodeComparisonViewActionContext {
/**
* Gets the CodeComparisonPanel associated with this context.
* @return the code comparison panel.
* Gets the view associated with this context.
* @return the code comparison provider.
*/
public abstract CodeComparisonPanel getCodeComparisonPanel();
public abstract CodeComparisonView getCodeComparisonView();
}

View file

@ -0,0 +1,87 @@
/* ###
* 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.features.base.codecompare.panel;
import java.util.*;
import java.util.Map.Entry;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.PluginTool;
/**
* A state object to save settings each type of comparison view known by the system. This class
* is meant to be used to allow user settings to be applied to each new comparison widget that is
* created. Also, the class allows the tool to save those settings when the tool is saved.
* <p>
* When a comparison provider updates its save state object, it should call
* {@link PluginTool#setConfigChanged(boolean)} so that tool knows there are settings to be saved.
*/
public class CodeComparisonViewState {
private static final String FUNCTION_COMPARISON_STATES = "CodeComparisonStates";
private Map<Class<? extends CodeComparisonView>, SaveState> states = new HashMap<>();
public SaveState getSaveState(Class<? extends CodeComparisonView> clazz) {
return states.computeIfAbsent(clazz, this::createSaveState);
}
private SaveState createSaveState(Class<? extends CodeComparisonView> clazz) {
return new SaveState();
}
/**
* Called by the tool to write the panels' saved states into the tools save state
* @param saveState the tool's save state
*/
public void writeConfigState(SaveState saveState) {
Set<Entry<Class<? extends CodeComparisonView>, SaveState>> entries = states.entrySet();
SaveState classStates = new SaveState();
for (Entry<Class<? extends CodeComparisonView>, SaveState> entry : entries) {
Class<? extends CodeComparisonView> clazz = entry.getKey();
SaveState subState = entry.getValue();
classStates.putSaveState(clazz.getName(), subState);
}
saveState.putSaveState(FUNCTION_COMPARISON_STATES, classStates);
}
/**
* Called by the tool to load saved state for the comparison providers
* @param saveState the tool's state
*/
public void readConfigState(SaveState saveState) {
SaveState classStates = saveState.getSaveState(FUNCTION_COMPARISON_STATES);
if (classStates == null) {
return;
}
String[] names = classStates.getNames();
for (String className : names) {
try {
@SuppressWarnings("unchecked")
Class<? extends CodeComparisonView> clazz =
(Class<? extends CodeComparisonView>) Class.forName(className);
SaveState classState = classStates.getSaveState(className);
states.put(clazz, classState);
}
catch (ClassNotFoundException e) {
// ignore
}
}
}
}

View file

@ -24,7 +24,7 @@ import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
/**
* ComparisonData is an abstract of items that can be compared in a {@link CodeComparisonPanel}.
* ComparisonData is an abstraction of items that can be compared in a {@link CodeComparisonView}.
* Not all comparison panels can handle all types of comparison data. For example, the decompiler
* comparison only works when the comparison data is a function.
*/
@ -71,6 +71,7 @@ public interface ComparisonData {
/**
* Returns the initial program location to put the cursor when the panel is first displayed
* @return the location
*/
public ProgramLocation getInitialLocation();

View file

@ -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.
@ -21,7 +21,6 @@ import static ghidra.util.datastruct.Duo.Side.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.List;
@ -34,10 +33,9 @@ import docking.ComponentProvider;
import docking.action.*;
import docking.widgets.tabbedpane.DockingTabRenderer;
import generic.theme.GIcon;
import ghidra.features.base.codecompare.listing.ListingCodeComparisonPanel;
import ghidra.features.base.codecompare.listing.ListingCodeComparisonView;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.*;
import ghidra.util.HelpLocation;
@ -48,18 +46,16 @@ import help.Help;
import help.HelpService;
/**
* A panel for displaying {@link Function functions}, {@link Data data}, or
* {@link AddressSet address sets} side-by-side for comparison purposes
* A panel for displaying {@link Function functions} side-by-side for comparison purposes
*/
public class FunctionComparisonPanel extends JPanel implements ChangeListener {
private static final String ORIENTATION_PROPERTY_NAME = "ORIENTATION";
private static final String DEFAULT_CODE_COMPARISON_VIEW = ListingCodeComparisonPanel.NAME;
private static final String DEFAULT_CODE_COMPARISON_VIEW = ListingCodeComparisonView.NAME;
private static final String COMPARISON_VIEW_DISPLAYED = "COMPARISON_VIEW_DISPLAYED";
private static final String CODE_COMPARISON_LOCK_SCROLLING_TOGETHER =
"CODE_COMPARISON_LOCK_SCROLLING_TOGETHER";
private static final HelpService help = Help.getHelpService();
private static final String HELP_TOPIC = "FunctionComparison";
private static final Icon SYNC_SCROLLING_ICON =
@ -72,23 +68,40 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
private JTabbedPane tabbedPane;
private Map<String, JComponent> tabNameToComponentMap;
private List<CodeComparisonPanel> codeComparisonPanels;
private List<CodeComparisonView> codeComparisonViews;
private ToggleScrollLockAction toggleScrollLockAction;
private boolean syncScrolling = false;
private Duo<ComparisonData> comparisonData = new Duo<ComparisonData>();
public FunctionComparisonPanel(PluginTool tool, String owner) {
this.comparisonData = new Duo<>(EMPTY, EMPTY);
private FunctionComparisonState state;
codeComparisonPanels = getCodeComparisonPanels(tool, owner);
/**
* Constructor
* @param tool the tool
* @param owner the owner's name
* @param state the comparison save state
*/
public FunctionComparisonPanel(PluginTool tool, String owner, FunctionComparisonState state) {
this.comparisonData = new Duo<>(EMPTY, EMPTY);
this.state = state;
state.addUpdateCallback(this::comparisonStateUpdated);
codeComparisonViews = getCodeComparisonViews(tool, owner);
tabNameToComponentMap = new HashMap<>();
createMainPanel();
createActions(owner);
setScrollingSyncState(true);
HelpService help = Help.getHelpService();
help.registerHelp(this, new HelpLocation(HELP_TOPIC, "Function Comparison"));
}
private void comparisonStateUpdated() {
readPanelState();
readViewState();
}
/**
* Load the given functions into the views of this panel
*
@ -118,9 +131,9 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
public void loadComparisons(ComparisonData left, ComparisonData right) {
comparisonData = new Duo<>(left, right);
CodeComparisonPanel activePanel = getActiveComparisonPanel();
if (activePanel != null) {
activePanel.loadComparisons(left, right);
CodeComparisonView activeView = getActiveComparisonView();
if (activeView != null) {
activeView.loadComparisons(left, right);
}
}
@ -173,9 +186,9 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
// Setting the addresses to be displayed to null effectively clears
// the display
CodeComparisonPanel activePanel = getActiveComparisonPanel();
if (activePanel != null) {
activePanel.clearComparisons();
CodeComparisonView activeView = getActiveComparisonView();
if (activeView != null) {
activeView.clearComparisons();
}
}
@ -190,15 +203,15 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
}
/**
* Gets the ListingCodeComparisonPanel being displayed by this panel
* Gets the ListingCodeComparisonView being displayed by this panel
* if one exists
*
* @return the comparison panel or null
*/
public ListingCodeComparisonPanel getDualListingPanel() {
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
if (codeComparisonPanel instanceof ListingCodeComparisonPanel listingPanel) {
return listingPanel;
public ListingCodeComparisonView getDualListingView() {
for (CodeComparisonView view : codeComparisonViews) {
if (view instanceof ListingCodeComparisonView listingView) {
return listingView;
}
}
return null;
@ -207,13 +220,14 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
@Override
public void stateChanged(ChangeEvent e) {
tabChanged();
writeTabState();
}
/**
* Set the current tabbed panel to be the component with the given name
*
* @param name name of view to set as the current tab
* @return true if the named view was found in the provider map
* @return true if the named view was found in the view map
*/
public boolean setCurrentTabbedComponent(String name) {
@ -255,26 +269,34 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
tabbedPane.removeAll();
setVisible(false);
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
codeComparisonPanel.dispose();
for (CodeComparisonView view : codeComparisonViews) {
view.dispose();
}
}
public void programClosed(Program program) {
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
codeComparisonPanel.programClosed(program);
for (CodeComparisonView view : codeComparisonViews) {
view.programClosed(program);
}
}
public CodeComparisonPanel getCodeComparisonPanelByName(String name) {
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
if (name.equals(codeComparisonPanel.getName())) {
return codeComparisonPanel;
public CodeComparisonView getCodeComparisonView(String name) {
for (CodeComparisonView view : codeComparisonViews) {
if (name.equals(view.getName())) {
return view;
}
}
return null;
}
public void selectComparisonView(String name) {
for (CodeComparisonView view : codeComparisonViews) {
if (name.equals(view.getName())) {
tabbedPane.setSelectedComponent(view);
}
}
}
/**
* Create the main tabbed panel
*/
@ -287,22 +309,21 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
add(tabbedPane, BorderLayout.CENTER);
setPreferredSize(new Dimension(200, 300));
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
tabbedPane.add(codeComparisonPanel.getName(), codeComparisonPanel);
tabNameToComponentMap.put(codeComparisonPanel.getName(), codeComparisonPanel);
for (CodeComparisonView view : codeComparisonViews) {
tabbedPane.add(view.getName(), view);
tabNameToComponentMap.put(view.getName(), view);
}
}
/**
* Invoked when there is a tab change. This loads the active tab with
* the appropriate data to be compared.
* Invoked when there is a tab change. This loads the active tab with the data to be compared.
*/
private void tabChanged() {
CodeComparisonPanel activePanel = getActiveComparisonPanel();
if (activePanel == null) {
CodeComparisonView activeView = getActiveComparisonView();
if (activeView == null) {
return; // initializing
}
activePanel.loadComparisons(comparisonData.get(LEFT), comparisonData.get(RIGHT));
activeView.loadComparisons(comparisonData.get(LEFT), comparisonData.get(RIGHT));
}
/**
@ -311,70 +332,73 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
* @return the currently selected comparison panel, or null if nothing
* selected
*/
private CodeComparisonPanel getActiveComparisonPanel() {
return (CodeComparisonPanel) tabbedPane.getSelectedComponent();
private CodeComparisonView getActiveComparisonView() {
return (CodeComparisonView) tabbedPane.getSelectedComponent();
}
/**
* Sets up the FunctionComparisonPanel and which CodeComparisonPanel is currently
* displayed based on the specified saveState
*
* @param prefix identifier to prepend to any save state names to make them unique
* @param saveState the save state for retrieving information
*/
public void readConfigState(String prefix, SaveState saveState) {
private void readViewState() {
CodeComparisonViewState viewState = state.getViewState();
codeComparisonViews.forEach(v -> {
Class<? extends CodeComparisonView> viewClass = v.getClass();
SaveState saveState = viewState.getSaveState(viewClass);
v.setSaveState(saveState);
});
}
private void readPanelState() {
SaveState panelState = state.getPanelState();
String currentTabView =
saveState.getString(prefix + COMPARISON_VIEW_DISPLAYED, DEFAULT_CODE_COMPARISON_VIEW);
panelState.getString(COMPARISON_VIEW_DISPLAYED, DEFAULT_CODE_COMPARISON_VIEW);
setCurrentTabbedComponent(currentTabView);
setScrollingSyncState(
saveState.getBoolean(prefix + CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, true));
panelState.getBoolean(CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, true));
for (CodeComparisonPanel panel : codeComparisonPanels) {
String key = prefix + panel.getName() + ORIENTATION_PROPERTY_NAME;
panel.setSideBySide(saveState.getBoolean(key, true));
for (CodeComparisonView view : codeComparisonViews) {
String key = view.getName() + ORIENTATION_PROPERTY_NAME;
view.setSideBySide(panelState.getBoolean(key, true));
}
}
/**
* Saves the information to the save state about the FunctionComparisonPanel and
* which CodeComparisonPanel is currently displayed
*
* @param prefix identifier to prepend to any save state names to make them unique
* @param saveState the save state where the information gets written
*/
public void writeConfigState(String prefix, SaveState saveState) {
private void writeTabState() {
String currentComponentName = getCurrentComponentName();
if (currentComponentName != null) {
saveState.putString(prefix + COMPARISON_VIEW_DISPLAYED, getCurrentComponentName());
}
saveState.putBoolean(prefix + CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, isScrollingSynced());
for (CodeComparisonPanel panel : codeComparisonPanels) {
String key = prefix + panel.getName() + ORIENTATION_PROPERTY_NAME;
boolean sideBySide = panel.isSideBySide();
saveState.putBoolean(key, sideBySide);
if (currentComponentName == null) {
return;
}
SaveState panelState = state.getPanelState();
panelState.putString(COMPARISON_VIEW_DISPLAYED, getCurrentComponentName());
state.setChanged();
}
private void writeScrollState() {
SaveState panelState = state.getPanelState();
panelState.putBoolean(CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, isScrollingSynced());
state.setChanged();
}
private void writeOrientationState() {
SaveState panelState = state.getPanelState();
for (CodeComparisonView view : codeComparisonViews) {
String key = view.getName() + ORIENTATION_PROPERTY_NAME;
boolean sideBySide = view.isSideBySide();
panelState.putBoolean(key, sideBySide);
}
}
/**
* Gets all actions for the FunctionComparisonPanel and all CodeComparisonPanels in this
* FunctionComparisonPanel
*
* @return the code comparison actions
*/
public DockingAction[] getCodeComparisonActions() {
ArrayList<DockingAction> dockingActionList = new ArrayList<>();
// Get actions for this functionComparisonPanel
DockingAction[] functionComparisonActions = getActions();
for (DockingAction dockingAction : functionComparisonActions) {
dockingActionList.add(dockingAction);
// Get actions for this panel
DockingAction[] actions = getActions();
for (DockingAction action : actions) {
dockingActionList.add(action);
}
// Get actions for each CodeComparisonPanel
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
dockingActionList.addAll(codeComparisonPanel.getActions());
// Get actions for each view
for (CodeComparisonView view : codeComparisonViews) {
dockingActionList.addAll(view.getActions());
}
return dockingActionList.toArray(new DockingAction[dockingActionList.size()]);
@ -382,7 +406,7 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
/**
* Sets the prefixes that are to be prepended to the title displayed for each side of
* each CodeComparisonPanel
* each {@link CodeComparisonView}
*
* @param leftTitlePrefix the prefix to prepend to the left titles
* @param rightTitlePrefix the prefix to prepend to the right titles
@ -390,8 +414,8 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
public void setTitlePrefixes(String leftTitlePrefix, String rightTitlePrefix) {
Component[] components = tabbedPane.getComponents();
for (Component component : components) {
if (component instanceof CodeComparisonPanel) {
((CodeComparisonPanel) component).setTitlePrefixes(leftTitlePrefix,
if (component instanceof CodeComparisonView) {
((CodeComparisonView) component).setTitlePrefixes(leftTitlePrefix,
rightTitlePrefix);
}
}
@ -405,9 +429,9 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
* @return the action context
*/
public ActionContext getActionContext(MouseEvent event, ComponentProvider componentProvider) {
CodeComparisonPanel activePanel = getDisplayedPanel();
if (activePanel != null) {
return activePanel.getActionContext(componentProvider, event);
CodeComparisonView activeProvider = getDisplayedView();
if (activeProvider != null) {
return activeProvider.getActionContext(componentProvider, event);
}
return null;
}
@ -432,43 +456,46 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
if (isScrollingSynced() == syncScrolling) {
return;
}
toggleScrollLockAction.setSelected(syncScrolling);
toggleScrollLockAction.setToolBarData(new ToolBarData(
syncScrolling ? SYNC_SCROLLING_ICON : UNSYNC_SCROLLING_ICON, SCROLLING_GROUP));
// Notify each comparison panel of the scrolling sync state.
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
codeComparisonPanel.setSynchronizedScrolling(syncScrolling);
for (CodeComparisonView view : codeComparisonViews) {
view.setSynchronizedScrolling(syncScrolling);
}
this.syncScrolling = syncScrolling;
writeScrollState();
}
/**
* Gets the currently displayed CodeComparisonPanel
* Gets the currently displayed {@link CodeComparisonView}
*
* @return the current panel or null.
*/
public CodeComparisonPanel getDisplayedPanel() {
public CodeComparisonView getDisplayedView() {
int selectedIndex = tabbedPane.getSelectedIndex();
Component component = tabbedPane.getComponentAt(selectedIndex);
return (CodeComparisonPanel) component;
return (CodeComparisonView) component;
}
/**
* Updates the enablement for all actions provided by each panel
* Updates the enablement for all actions provided by each view
*/
public void updateActionEnablement() {
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
codeComparisonPanel.updateActionEnablement();
for (CodeComparisonView view : codeComparisonViews) {
view.updateActionEnablement();
}
}
/**
* Get the current code comparison panel being viewed
* Get the current code comparison view being viewed
*
* @return null if there is no code comparison panel
* @return null if there is no code comparison view
*/
public CodeComparisonPanel getCurrentComponent() {
return (CodeComparisonPanel) tabbedPane.getSelectedComponent();
public CodeComparisonView getCurrentView() {
return (CodeComparisonView) tabbedPane.getSelectedComponent();
}
/**
@ -519,45 +546,48 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
}
}
public List<CodeComparisonPanel> getComparisonPanels() {
return codeComparisonPanels;
public List<CodeComparisonView> getComparisonView() {
return codeComparisonViews;
}
/**
* Discovers the CodeComparisonPanels which are extension points
* Discovers the {@link CodeComparisonView}s which are extension points
*
* @return the CodeComparisonPanels which are extension points
* @return the views which are extension points
*/
private List<CodeComparisonPanel> getCodeComparisonPanels(PluginTool tool, String owner) {
if (codeComparisonPanels == null) {
codeComparisonPanels = createAllPossibleCodeComparisonPanels(tool, owner);
codeComparisonPanels.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));
private List<CodeComparisonView> getCodeComparisonViews(PluginTool tool, String owner) {
if (codeComparisonViews == null) {
codeComparisonViews = createAllCodeComparisonViews(tool, owner);
codeComparisonViews.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));
}
return codeComparisonPanels;
return codeComparisonViews;
}
private List<CodeComparisonPanel> createAllPossibleCodeComparisonPanels(PluginTool tool,
private List<CodeComparisonView> createAllCodeComparisonViews(PluginTool tool,
String owner) {
List<CodeComparisonPanel> instances = new ArrayList<>();
List<Class<? extends CodeComparisonPanel>> classes =
ClassSearcher.getClasses(CodeComparisonPanel.class);
for (Class<? extends CodeComparisonPanel> panelClass : classes) {
CodeComparisonViewState viewState = state.getViewState();
List<CodeComparisonView> instances = new ArrayList<>();
List<Class<? extends CodeComparisonView>> classes =
ClassSearcher.getClasses(CodeComparisonView.class);
for (Class<? extends CodeComparisonView> viewClass : classes) {
try {
Constructor<? extends CodeComparisonPanel> constructor =
panelClass.getConstructor(String.class, PluginTool.class);
CodeComparisonPanel panel = constructor.newInstance(owner, tool);
instances.add(panel);
Constructor<? extends CodeComparisonView> constructor =
viewClass.getConstructor(String.class, PluginTool.class);
CodeComparisonView view = constructor.newInstance(owner, tool);
SaveState saveState = viewState.getSaveState(viewClass);
view.setSaveState(saveState);
view.setOrientationChangedCallback(() -> writeOrientationState());
instances.add(view);
}
catch (NoSuchMethodException | SecurityException | InstantiationException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
catch (Exception e) {
Msg.showError(this, null, "Error Creating Extension Point",
"Error creating class " + panelClass.getName() +
"Error creating class " + viewClass.getName() +
" when creating extension points for " +
CodeComparisonPanel.class.getName(),
CodeComparisonView.class.getName(),
e);
}
}

View file

@ -0,0 +1,90 @@
/* ###
* 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.features.base.codecompare.panel;
import java.util.*;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.PluginTool;
import utility.function.Callback;
/**
* An object to share config state between providers and all views within those providers.
* <p>
* When a comparison provider updates its save state object, it should call
* {@link PluginTool#setConfigChanged(boolean)} so that tool knows there are settings to be saved.
*/
public class FunctionComparisonState {
private static final String PROVIDER_SAVE_STATE_NAME = "FunctionComparison";
private SaveState panelState = new SaveState();
private CodeComparisonViewState comparisonState = new CodeComparisonViewState();
private PluginTool tool;
private List<Callback> updateCallbacks = new ArrayList<>();
public FunctionComparisonState(PluginTool tool) {
this.tool = tool;
}
/**
* Returns the state object for the provider
* @return the state object for the provider
*/
public SaveState getPanelState() {
return panelState;
}
/**
* Returns the save state object for the views that live inside a provider
* @return the state
*/
public CodeComparisonViewState getViewState() {
return comparisonState;
}
/**
* Signals to the tool that there are changes to the config state that can be saved.
*/
public void setChanged() {
tool.setConfigChanged(true);
}
public void writeConfigState(SaveState saveState) {
saveState.putSaveState(PROVIDER_SAVE_STATE_NAME, panelState);
comparisonState.writeConfigState(saveState);
}
public void readConfigState(SaveState saveState) {
SaveState restoredPanelState = saveState.getSaveState(PROVIDER_SAVE_STATE_NAME);
if (restoredPanelState != null) {
panelState = restoredPanelState;
}
comparisonState.readConfigState(saveState);
updateCallbacks.forEach(Callback::call);
}
/**
* Adds a callback to this state that is notified when this state changes.
* @param callback the callback
*/
public void addUpdateCallback(Callback callback) {
updateCallbacks.add(Objects.requireNonNull(callback));
}
}

View file

@ -304,7 +304,7 @@ class MemorySearchControlPanel extends JPanel {
return;
}
DockingUtils.setTipWindowEnabled(false);
setMyToolTipsEnabled(false);
Point location = searchInputField.getLocation();
adjustLocationForCaretPosition(location);
@ -326,7 +326,7 @@ class MemorySearchControlPanel extends JPanel {
private void clearInputError() {
errorMessage = null;
DockingUtils.setTipWindowEnabled(true);
setMyToolTipsEnabled(true);
PopupWindow.hideAllWindows();
if (popup != null) {
popup.dispose();
@ -336,6 +336,34 @@ class MemorySearchControlPanel extends JPanel {
}
}
private void setMyToolTipsEnabled(boolean enabled) {
if (!DockingUtils.isTipWindowEnabled()) {
return;
}
ToolTipManager ttm = ToolTipManager.sharedInstance();
doSetMyToolTipsEnabled(this, ttm, enabled);
}
private void doSetMyToolTipsEnabled(JComponent c, ToolTipManager ttm, boolean enabled) {
if (enabled) {
ttm.registerComponent(c);
}
else {
ttm.unregisterComponent(c);
}
Component[] children = c.getComponents();
for (Component child : children) {
if (child instanceof JComponent) {
doSetMyToolTipsEnabled((JComponent) child, ttm, enabled);
}
}
}
private void updateCombo() {
ByteMatcher[] historyArray = searchHistory.getHistoryAsArray();

View file

@ -82,6 +82,7 @@ public class ProgramSelection implements AddressSetView {
* @param addressFactory NOT USED
* @param from the start of the selection
* @param to the end of the selection
* @deprecated use {@link #ProgramSelection(Address, Address)}
*/
@Deprecated(since = "11.2", forRemoval = true)
public ProgramSelection(AddressFactory addressFactory, Address from, Address to) {

View file

@ -55,7 +55,8 @@ import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Program;
import ghidra.program.util.DefaultLanguageService;
import ghidra.program.util.ProgramUtilities;
import ghidra.util.*;
import ghidra.util.Msg;
import ghidra.util.TaskUtilities;
import ghidra.util.datastruct.WeakSet;
import ghidra.util.exception.*;
import ghidra.util.task.*;
@ -75,7 +76,7 @@ public class TestEnv {
private static Set<TestEnv> instances = new HashSet<>();
private FrontEndTool frontEndTool;
private PluginTool tool;
protected PluginTool tool;
private static TestProgramManager programManager = new TestProgramManager();
@ -962,7 +963,7 @@ public class TestEnv {
public Program loadResourceProgramAsBinary(String programName, Language language,
CompilerSpec compilerSpec) throws LanguageNotFoundException, IOException,
CancelledException, DuplicateNameException, InvalidNameException, VersionException {
CancelledException, VersionException {
File file = AbstractGenericTest.getTestDataFile(programName);
if (file == null || !file.exists()) {
throw new FileNotFoundException("Can not find test program: " + programName);
@ -971,8 +972,7 @@ public class TestEnv {
}
public Program loadResourceProgramAsBinary(String programName, Processor processor)
throws CancelledException, DuplicateNameException, InvalidNameException,
VersionException, IOException {
throws CancelledException, VersionException, IOException {
Language language =
DefaultLanguageService.getLanguageService().getDefaultLanguage(processor);
CompilerSpec compilerSpec = language.getDefaultCompilerSpec();

View file

@ -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.
@ -41,7 +41,7 @@ public class NamespaceTableColumn
return parentPath == null ? GlobalNamespace.GLOBAL_NAMESPACE_NAME : parentPath;
}
Symbol symbol = getSymbol(rowObject, program);
if (symbol != null) {
if (symbol != null && !symbol.isDeleted() && !symbol.isDynamic()) {
return symbol.getParentNamespace().getName(true);
}
Function function =

View file

@ -87,7 +87,7 @@ import resources.ResourceManager;
public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIntegrationTest {
private static final String SCREENSHOT_USER_NAME = "User-1";
protected static final String SCREENSHOT_USER_NAME = "User-1";
static {
System.setProperty("user.name", "User-1");

View file

@ -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.
@ -21,6 +21,9 @@ import org.jdom.Element;
import org.junit.Before;
import org.junit.Test;
import ghidra.util.Msg;
import ghidra.util.xml.XmlUtilities;
public class SaveStateTest {
private SaveState saveState;
@ -46,6 +49,16 @@ public class SaveStateTest {
assertEquals(2, restoreSubState.getNames().length);
assertEquals(5, restoreSubState.getInt("a", 0));
assertEquals("bar", restoreSubState.getString("foo", ""));
SaveState s1 = new SaveState("Parent");
SaveState c1 = new SaveState("Child1");
c1.putBoolean("Bool1", false);
c1.putString("String1", "Hey bob");
s1.putSaveState("MapChildName1", c1);
Element e = s1.saveToXml();
String s = XmlUtilities.toString(e);
Msg.debug(this, s);
}
private SaveState saveAndRestoreToXml() throws Exception {

View file

@ -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,4 +23,5 @@ eclipse.project.name = 'Features CodeCompare'
dependencies {
api project(":Decompiler")
api project(":FunctionGraph")
}

View file

@ -26,7 +26,7 @@ import ghidra.util.Msg;
/**
* Subclass of {@link AbstractMatchedTokensAction} for actions in a
* {@link DecompilerCodeComparisonPanel} that are available only when the matched tokens are
* {@link DecompilerCodeComparisonView} that are available only when the matched tokens are
* function calls
*/
public abstract class AbstractMatchedCalleeTokensAction extends AbstractMatchedTokensAction {
@ -37,12 +37,12 @@ public abstract class AbstractMatchedCalleeTokensAction extends AbstractMatchedT
*
* @param actionName name of action
* @param owner owner of action
* @param diffPanel diff panel containing action
* @param comparisonProvider diff comparison provider containing action
* @param disableOnReadOnly if true, action will be disabled for read-only programs
*/
public AbstractMatchedCalleeTokensAction(String actionName, String owner,
DecompilerCodeComparisonPanel diffPanel, boolean disableOnReadOnly) {
super(actionName, owner, diffPanel, disableOnReadOnly);
DecompilerCodeComparisonView comparisonProvider, boolean disableOnReadOnly) {
super(actionName, owner, comparisonProvider, disableOnReadOnly);
}
@Override
@ -69,15 +69,15 @@ public abstract class AbstractMatchedCalleeTokensAction extends AbstractMatchedT
@Override
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
DecompilerCodeComparisonPanel decompPanel = context.getCodeComparisonPanel();
DecompilerCodeComparisonView provider = context.getCodeComparisonView();
TokenPair currentPair = context.getTokenPair();
ClangFuncNameToken leftFuncToken = (ClangFuncNameToken) currentPair.leftToken();
ClangFuncNameToken rightFuncToken = (ClangFuncNameToken) currentPair.rightToken();
Function leftFunction = getFuncFromToken(leftFuncToken, decompPanel.getProgram(LEFT));
Function rightFunction = getFuncFromToken(rightFuncToken, decompPanel.getProgram(RIGHT));
Function leftFunction = getFuncFromToken(leftFuncToken, provider.getProgram(LEFT));
Function rightFunction = getFuncFromToken(rightFuncToken, provider.getProgram(RIGHT));
if (leftFunction == null || rightFunction == null) {
return;
}

View file

@ -19,13 +19,13 @@ import docking.ActionContext;
import docking.action.DockingAction;
/**
* This is a base class for actions in a {@link DecompilerCodeComparisonPanel}
* This is a base class for actions in a {@link DecompilerCodeComparisonView}
*/
public abstract class AbstractMatchedTokensAction extends DockingAction {
protected static final String MENU_PARENT = "Apply From Other Function";
protected static final String HELP_TOPIC = "FunctionComparison";
protected DecompilerCodeComparisonPanel diffPanel;
protected DecompilerCodeComparisonView comparisonProvider;
protected boolean disableOnReadOnly;
/**
@ -33,13 +33,13 @@ public abstract class AbstractMatchedTokensAction extends DockingAction {
*
* @param actionName name of action
* @param owner owner of action
* @param diffPanel diff panel containing action
* @param comparisonProvider diff panel containing action
* @param disableOnReadOnly if true, action will be disabled for read-only programs
*/
public AbstractMatchedTokensAction(String actionName, String owner,
DecompilerCodeComparisonPanel diffPanel, boolean disableOnReadOnly) {
DecompilerCodeComparisonView comparisonProvider, boolean disableOnReadOnly) {
super(actionName, owner);
this.diffPanel = diffPanel;
this.comparisonProvider = comparisonProvider;
this.disableOnReadOnly = disableOnReadOnly;
}

View file

@ -33,14 +33,9 @@ public class ApplyCalleeEmptySignatureFromMatchedTokensAction
private PluginTool tool;
public static final String ACTION_NAME = "Function Comparison Apply Callee Signature";
/**
* Construtor
* @param diffPanel diff panel
* @param tool tool
*/
public ApplyCalleeEmptySignatureFromMatchedTokensAction(
DecompilerCodeComparisonPanel diffPanel, PluginTool tool) {
super(ACTION_NAME, tool.getName(), diffPanel, true);
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
this.tool = tool;
MenuData menuData =
@ -53,7 +48,7 @@ public class ApplyCalleeEmptySignatureFromMatchedTokensAction
@Override
protected void doCalleeActionPerformed(Function leftFunction, Function rightFunction) {
Side activeSide = diffPanel.getActiveSide();
Side activeSide = comparisonProvider.getActiveSide();
Function activeFunction = activeSide == Side.LEFT ? leftFunction : rightFunction;
Function otherFunction = activeSide == Side.LEFT ? rightFunction : leftFunction;

View file

@ -33,14 +33,9 @@ public class ApplyCalleeFunctionNameFromMatchedTokensAction
private PluginTool tool;
public static final String ACTION_NAME = "Function Comparison Apply Callee Name";
/**
* Construtor
* @param diffPanel diff panel
* @param tool tool
*/
public ApplyCalleeFunctionNameFromMatchedTokensAction(
DecompilerCodeComparisonPanel diffPanel, PluginTool tool) {
super(ACTION_NAME, tool.getName(), diffPanel, true);
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
this.tool = tool;
MenuData menuData =
@ -52,7 +47,7 @@ public class ApplyCalleeFunctionNameFromMatchedTokensAction
@Override
protected void doCalleeActionPerformed(Function leftFunction, Function rightFunction) {
Side activeSide = diffPanel.getActiveSide();
Side activeSide = comparisonProvider.getActiveSide();
Function activeFunction = activeSide == Side.LEFT ? leftFunction : rightFunction;
Function otherFunction = activeSide == Side.LEFT ? rightFunction : leftFunction;

View file

@ -33,14 +33,9 @@ public class ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction
public static final String ACTION_NAME =
"Function Comparison Apply Callee Signature And Datatypes";
/**
* Construtor
* @param diffPanel diff panel
* @param tool tool
*/
public ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction(
DecompilerCodeComparisonPanel diffPanel, PluginTool tool) {
super(ACTION_NAME, tool.getName(), diffPanel, true);
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
this.tool = tool;
MenuData menuData =
@ -53,7 +48,7 @@ public class ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction
@Override
protected void doCalleeActionPerformed(Function leftFunction, Function rightFunction) {
Side activeSide = diffPanel.getActiveSide();
Side activeSide = comparisonProvider.getActiveSide();
Function activeFunction = activeSide == Side.LEFT ? leftFunction : rightFunction;
Function otherFunction = activeSide == Side.LEFT ? rightFunction : leftFunction;

View file

@ -35,9 +35,9 @@ public class ApplyEmptyVariableTypeFromMatchedTokensAction extends AbstractMatch
public static final String ACTION_NAME = "Function Comparison Apply Variable Skeleton Type";
private static final String MENU_GROUP = "A1_ApplyVariable";
public ApplyEmptyVariableTypeFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
PluginTool tool) {
super(ACTION_NAME, tool.getName(), diffPanel, true);
public ApplyEmptyVariableTypeFromMatchedTokensAction(
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
this.tool = tool;
MenuData menuData =
@ -73,7 +73,7 @@ public class ApplyEmptyVariableTypeFromMatchedTokensAction extends AbstractMatch
protected void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
TokenPair currentPair = context.getTokenPair();
Side activeSide = diffPanel.getActiveSide();
Side activeSide = comparisonProvider.getActiveSide();
ClangVariableToken activeToken =
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
@ -87,7 +87,7 @@ public class ApplyEmptyVariableTypeFromMatchedTokensAction extends AbstractMatch
HighSymbol otherHighSymbol =
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
Function activeFunction = context.getCodeComparisonPanel().getFunction(activeSide);
Function activeFunction = context.getCodeComparisonView().getFunction(activeSide);
Program activeProgram = activeFunction.getProgram();
DataType dt = otherHighSymbol.getDataType();

View file

@ -36,14 +36,9 @@ public class ApplyGlobalNameFromMatchedTokensAction extends AbstractMatchedToken
public static final String ACTION_NAME = "Function Comparison Apply Global Variable Name";
private static final String MENU_GROUP = "A1_ApplyVariable";
/**
* Construtor
* @param diffPanel diff panel
* @param tool tool
*/
public ApplyGlobalNameFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
PluginTool tool) {
super(ACTION_NAME, tool.getName(), diffPanel, true);
public ApplyGlobalNameFromMatchedTokensAction(
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
this.tool = tool;
MenuData menuData =
@ -83,7 +78,7 @@ public class ApplyGlobalNameFromMatchedTokensAction extends AbstractMatchedToken
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
TokenPair currentPair = context.getTokenPair();
Side activeSide = diffPanel.getActiveSide();
Side activeSide = comparisonProvider.getActiveSide();
ClangVariableToken activeToken =
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
: (ClangVariableToken) currentPair.rightToken();
@ -96,7 +91,7 @@ public class ApplyGlobalNameFromMatchedTokensAction extends AbstractMatchedToken
HighSymbol otherHighSymbol =
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
Program activeProgram = context.getCodeComparisonPanel().getProgram(activeSide);
Program activeProgram = context.getCodeComparisonView().getProgram(activeSide);
Symbol activeSymbol = null;
if (activeHighSymbol instanceof HighCodeSymbol activeCodeSymbol) {

View file

@ -36,14 +36,9 @@ public class ApplyLocalNameFromMatchedTokensAction extends AbstractMatchedTokens
public static final String ACTION_NAME = "Function Comparison Apply Local Variable Name";
private static final String MENU_GROUP = "A1_ApplyVariable";
/**
* Construtor
* @param diffPanel diff panel
* @param tool tool
*/
public ApplyLocalNameFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
PluginTool tool) {
super(ACTION_NAME, tool.getName(), diffPanel, true);
public ApplyLocalNameFromMatchedTokensAction(
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
this.tool = tool;
MenuData menuData =
@ -82,7 +77,7 @@ public class ApplyLocalNameFromMatchedTokensAction extends AbstractMatchedTokens
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
TokenPair currentPair = context.getTokenPair();
Side activeSide = diffPanel.getActiveSide();
Side activeSide = comparisonProvider.getActiveSide();
ClangVariableToken activeToken =
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
@ -96,7 +91,7 @@ public class ApplyLocalNameFromMatchedTokensAction extends AbstractMatchedTokens
HighSymbol otherHighSymbol =
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
Function activeFunction = context.getCodeComparisonPanel().getFunction(activeSide);
Function activeFunction = context.getCodeComparisonView().getFunction(activeSide);
Program activeProgram = activeFunction.getProgram();
try {

View file

@ -37,14 +37,9 @@ public class ApplyVariableTypeFromMatchedTokensAction extends AbstractMatchedTok
public static final String ACTION_NAME = "Function Comparison Apply Variable Type";
private static final String MENU_GROUP = "A1_ApplyVariable";
/**
* Construtor
* @param diffPanel diff panel
* @param tool tool
*/
public ApplyVariableTypeFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
PluginTool tool) {
super(ACTION_NAME, tool.getName(), diffPanel, true);
public ApplyVariableTypeFromMatchedTokensAction(
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
this.tool = tool;
MenuData menuData =
@ -80,7 +75,7 @@ public class ApplyVariableTypeFromMatchedTokensAction extends AbstractMatchedTok
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
TokenPair currentPair = context.getTokenPair();
Side activeSide = diffPanel.getActiveSide();
Side activeSide = comparisonProvider.getActiveSide();
ClangVariableToken activeToken =
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
@ -94,7 +89,7 @@ public class ApplyVariableTypeFromMatchedTokensAction extends AbstractMatchedTok
HighSymbol otherHighSymbol =
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
Function activeFunction = context.getCodeComparisonPanel().getFunction(activeSide);
Function activeFunction = context.getCodeComparisonView().getFunction(activeSide);
Program activeProgram = activeFunction.getProgram();
DataType dt = otherHighSymbol.getDataType();

View file

@ -39,7 +39,6 @@ import ghidra.program.util.ProgramLocation;
public class CDisplay {
private final static String OPTIONS_TITLE = "Decompiler";
private ServiceProvider serviceProvider;
private DecompilerController controller;
private DecompileOptions decompileOptions;
private FieldLocation lastCursorPosition;
@ -53,7 +52,6 @@ public class CDisplay {
DecompileResultsListener decompileListener,
Consumer<ProgramLocation> locationConsumer) {
this.serviceProvider = serviceProvider;
highlightController = new DiffClangHighlightController(comparisonOptions);
decompileOptions = new DecompileOptions();
@ -138,10 +136,6 @@ public class CDisplay {
controller.dispose();
}
public DecompilerController getController() {
return controller;
}
public void refresh() {
saveCursorPosition();
DecompileData data = getDecompileData();
@ -176,8 +170,8 @@ public class CDisplay {
}
ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
ToolOptions options = tool.getOptions(OPTIONS_TITLE);
Program program = function == null ? null : function.getProgram();
decompileOptions.grabFromToolAndProgram(fieldOptions, options, program);
Program p = function == null ? null : function.getProgram();
decompileOptions.grabFromToolAndProgram(fieldOptions, options, p);
}
DiffClangHighlightController getHighlightController() {

View file

@ -35,7 +35,7 @@ public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedCalleeTo
* @param diffPanel diff Panel
* @param tool tool
*/
public CompareFuncsFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
public CompareFuncsFromMatchedTokensAction(DecompilerCodeComparisonView diffPanel,
PluginTool tool) {
super(ACTION_NAME, tool.getName(), diffPanel, false);
this.tool = tool;

View file

@ -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.
@ -151,4 +151,9 @@ public class DecompilerCodeComparisonOptions implements OptionsChangeListener {
optionsChangedCallback.call();
}
public void dispose(PluginTool tool) {
ToolOptions options =
tool.getOptions(DecompilerCodeComparisonOptions.OPTIONS_CATEGORY_NAME);
options.removeOptionsChangeListener(this);
}
}

View file

@ -32,7 +32,7 @@ import docking.options.OptionsService;
import generic.theme.GIcon;
import ghidra.app.decompiler.component.DecompileData;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
import ghidra.features.base.codecompare.panel.CodeComparisonView;
import ghidra.features.codecompare.graphanalysis.TokenBin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
@ -49,10 +49,9 @@ import resources.Icons;
import resources.MultiIcon;
/**
* Panel that displays two decompilers for comparison
* UI that displays two decompilers for comparison
*/
public class DecompilerCodeComparisonPanel
extends CodeComparisonPanel {
public class DecompilerCodeComparisonView extends CodeComparisonView {
public static final String NAME = "Decompiler View";
@ -61,7 +60,7 @@ public class DecompilerCodeComparisonPanel
private Duo<CDisplay> cDisplays = new Duo<>();
private DecompilerCodeComparisonOptions comparisonOptions;
private CodeDiffFieldPanelCoordinator coordinator;
private DualDecompilerScrollCoordinator coordinator;
private DecompileDataDiff decompileDataDiff;
private ToggleExactConstantMatching toggleExactConstantMatchingAction;
@ -74,7 +73,7 @@ public class DecompilerCodeComparisonPanel
* @param owner the owner of this panel
* @param tool the tool displaying this panel
*/
public DecompilerCodeComparisonPanel(String owner, PluginTool tool) {
public DecompilerCodeComparisonView(String owner, PluginTool tool) {
super(owner, tool);
comparisonOptions = new DecompilerCodeComparisonOptions(tool, () -> repaint());
@ -119,7 +118,7 @@ public class DecompilerCodeComparisonPanel
public void dispose() {
setSynchronizedScrolling(false); // disposes any exiting coordinator
cDisplays.each(CDisplay::dispose);
comparisonOptions = null;
comparisonOptions.dispose(tool);
}
/**
@ -189,6 +188,7 @@ public class DecompilerCodeComparisonPanel
actions.add(new ApplyCalleeFunctionNameFromMatchedTokensAction(this, tool));
actions.add(new ApplyCalleeEmptySignatureFromMatchedTokensAction(this, tool));
actions.add(new ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction(this, tool));
}
private void decompileDataSet(Side side, DecompileData dcompileData) {
@ -272,8 +272,9 @@ public class DecompilerCodeComparisonPanel
}
}
private CodeDiffFieldPanelCoordinator createCoordinator() {
CodeDiffFieldPanelCoordinator panelCoordinator = new CodeDiffFieldPanelCoordinator(this);
private DualDecompilerScrollCoordinator createCoordinator() {
DualDecompilerScrollCoordinator panelCoordinator =
new DualDecompilerScrollCoordinator(this);
if (decompileDataDiff != null) {
TaskBuilder.withRunnable(monitor -> {
try {
@ -371,7 +372,7 @@ public class DecompilerCodeComparisonPanel
this.setToolBarData(new ToolBarData(NO_EXACT_CONSTANT_MATCHING_ICON, "toggles"));
setDescription(HTMLUtilities.toHTML("Toggle whether or not constants must\n" +
"be exactly the same value to be a match\n" + "in the Decomiler Diff View."));
"be exactly the same value to be a match\nin the Decomiler Diff View."));
setSelected(false);
setEnabled(true);
}

View file

@ -67,11 +67,11 @@ public class DecompilerDiffViewFindAction extends DockingAction {
@Override
public void actionPerformed(ActionContext context) {
DualDecompilerActionContext dualContext = (DualDecompilerActionContext) context;
DecompilerCodeComparisonPanel decompilerCompPanel =
dualContext.getCodeComparisonPanel();
DecompilerCodeComparisonView provider =
dualContext.getCodeComparisonView();
Side focusedSide = decompilerCompPanel.getActiveSide();
DecompilerPanel focusedPanel = decompilerCompPanel.getDecompilerPanel(focusedSide);
Side focusedSide = provider.getActiveSide();
DecompilerPanel focusedPanel = provider.getDecompilerPanel(focusedSide);
FindDialog dialog = findDialogs.get(focusedSide);
if (dialog == null) {
dialog = createFindDialog(focusedPanel, focusedSide);

View file

@ -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.
@ -33,12 +33,12 @@ public class DetermineDecompilerDifferencesTask extends Task {
private DecompileDataDiff decompileDataDiff;
private CodeDiffFieldPanelCoordinator decompilerFieldPanelCoordinator;
private DualDecompilerScrollCoordinator decompilerFieldPanelCoordinator;
public DetermineDecompilerDifferencesTask(DecompileDataDiff decompileDataDiff,
boolean matchConstantsExactly, DiffClangHighlightController leftHighlightController,
DiffClangHighlightController rightHighlightController,
CodeDiffFieldPanelCoordinator decompilerFieldPanelCoordinator) {
DualDecompilerScrollCoordinator decompilerFieldPanelCoordinator) {
super("Mapping C Tokens Between Functions", true, true, true);
this.decompileDataDiff = decompileDataDiff;

View file

@ -18,7 +18,6 @@ package ghidra.features.codecompare.decompile;
import static ghidra.util.datastruct.Duo.Side.*;
import java.awt.Component;
import java.util.Iterator;
import java.util.List;
import docking.ComponentProvider;
@ -38,26 +37,26 @@ import ghidra.util.datastruct.Duo.Side;
public class DualDecompilerActionContext extends CodeComparisonActionContext
implements RestrictedAddressSetContext {
private DecompilerCodeComparisonPanel decompilerComparisonPanel = null;
private DecompilerCodeComparisonView comparisonProvider = null;
private TokenPair tokenPair;
private boolean overrideReadOnly = false;
/**
* Creates an action context for a dual decompiler panel.
* @param provider the provider for this context
* @param panel the DecompilerComparisonPanel
* @param comparisonProvider the DecompilerComparisonPanel
* @param source the source of the action
*/
public DualDecompilerActionContext(ComponentProvider provider,
DecompilerCodeComparisonPanel panel, Component source) {
super(provider, panel, source);
decompilerComparisonPanel = panel;
DecompilerCodeComparisonView comparisonProvider, Component source) {
super(provider, comparisonProvider, source);
this.comparisonProvider = comparisonProvider;
tokenPair = computeTokenPair();
}
private TokenPair computeTokenPair() {
DecompilerPanel focusedPanel =
decompilerComparisonPanel.getActiveDisplay().getDecompilerPanel();
comparisonProvider.getActiveDisplay().getDecompilerPanel();
if (!(focusedPanel.getCurrentLocation() instanceof DecompilerLocation focusedLocation)) {
return null;
@ -67,7 +66,7 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
if (focusedToken == null) {
return null;
}
List<TokenBin> tokenBin = decompilerComparisonPanel.getHighBins();
List<TokenBin> tokenBin = comparisonProvider.getHighBins();
if (tokenBin == null) {
return null;
}
@ -80,13 +79,9 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
return null;
}
//loop over the tokens in the matching bin and return the first one in the same
//class as focusedToken
Iterator<ClangToken> tokenIter = matchedBin.iterator();
while (tokenIter.hasNext()) {
ClangToken currentMatch = tokenIter.next();
for (ClangToken currentMatch : matchedBin) {
if (currentMatch.getClass().equals(focusedToken.getClass())) {
return decompilerComparisonPanel.getActiveSide() == LEFT
return comparisonProvider.getActiveSide() == LEFT
? new TokenPair(focusedToken, currentMatch)
: new TokenPair(currentMatch, focusedToken);
}
@ -95,12 +90,12 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
}
/**
* Returns the {@link DecompilerCodeComparisonPanel} that generated this context
* Returns the {@link DecompilerCodeComparisonView} that generated this context
* @return the decompiler comparison panel that generated this context
*/
@Override
public DecompilerCodeComparisonPanel getCodeComparisonPanel() {
return decompilerComparisonPanel;
public DecompilerCodeComparisonView getCodeComparisonView() {
return comparisonProvider;
}
/**
@ -111,7 +106,7 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
* context
*/
public HighFunction getHighFunction(Side side) {
return decompilerComparisonPanel.getDecompilerPanel(side).getController().getHighFunction();
return comparisonProvider.getDecompilerPanel(side).getController().getHighFunction();
}
/**
@ -144,7 +139,7 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
}
Program activeProgram =
decompilerComparisonPanel.getProgram(decompilerComparisonPanel.getActiveSide());
comparisonProvider.getProgram(comparisonProvider.getActiveSide());
if (activeProgram == null) {
return true;

View file

@ -1,35 +0,0 @@
/* ###
* 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.features.codecompare.decompile;
import static ghidra.util.datastruct.Duo.Side.*;
import docking.widgets.fieldpanel.FieldPanel;
import docking.widgets.fieldpanel.internal.LineLockedFieldPanelCoordinator;
import ghidra.program.util.ProgramLocation;
abstract public class DualDecompilerFieldPanelCoordinator extends LineLockedFieldPanelCoordinator {
public DualDecompilerFieldPanelCoordinator(
DecompilerCodeComparisonPanel dualDecompilerPanel) {
super(new FieldPanel[] { dualDecompilerPanel.getDecompilerPanel(LEFT).getFieldPanel(),
dualDecompilerPanel.getDecompilerPanel(RIGHT).getFieldPanel() });
}
abstract public void leftLocationChanged(ProgramLocation leftLocation);
abstract public void rightLocationChanged(ProgramLocation rightLocation);
}

View file

@ -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.
@ -24,6 +24,8 @@ import java.util.List;
import org.apache.commons.collections4.BidiMap;
import org.apache.commons.collections4.bidimap.DualHashBidiMap;
import docking.widgets.fieldpanel.FieldPanel;
import docking.widgets.fieldpanel.internal.LineLockedFieldPanelScrollCoordinator;
import docking.widgets.fieldpanel.support.ViewerPosition;
import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.DecompilerPanel;
@ -33,11 +35,7 @@ import ghidra.program.util.ProgramLocation;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Class to coordinate the scrolling of two decompiler panels as well as cursor location
* highlighting due to cursor location changes.
*/
public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoordinator {
public class DualDecompilerScrollCoordinator extends LineLockedFieldPanelScrollCoordinator {
private BidiMap<Integer, Integer> leftToRightLineNumberPairing;
private List<ClangLine> leftLines = new ArrayList<ClangLine>();
@ -51,12 +49,13 @@ public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoord
/**
* Constructor
* @param dualDecompilerPanel decomp comparison panel
* @param comparisonProvider decomp comparison provider
*/
public CodeDiffFieldPanelCoordinator(DecompilerCodeComparisonPanel dualDecompilerPanel) {
super(dualDecompilerPanel);
this.leftDecompilerPanel = dualDecompilerPanel.getDecompilerPanel(LEFT);
this.rightDecompilerPanel = dualDecompilerPanel.getDecompilerPanel(RIGHT);
public DualDecompilerScrollCoordinator(DecompilerCodeComparisonView comparisonProvider) {
super(new FieldPanel[] { comparisonProvider.getDecompilerPanel(LEFT).getFieldPanel(),
comparisonProvider.getDecompilerPanel(RIGHT).getFieldPanel() });
this.leftDecompilerPanel = comparisonProvider.getDecompilerPanel(LEFT);
this.rightDecompilerPanel = comparisonProvider.getDecompilerPanel(RIGHT);
leftToRightLineNumberPairing = new DualHashBidiMap<>();
}
@ -89,7 +88,6 @@ public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoord
}
}
@Override
public void leftLocationChanged(ProgramLocation leftLocation) {
DecompilerLocation leftDecompilerLocation = (DecompilerLocation) leftLocation;
@ -107,7 +105,6 @@ public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoord
panelViewChanged(leftDecompilerPanel);
}
@Override
public void rightLocationChanged(ProgramLocation rightLocation) {
DecompilerLocation rightDecompilerLocation = (DecompilerLocation) rightLocation;
@ -301,5 +298,4 @@ public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoord
return true;
}
}

View file

@ -0,0 +1,54 @@
/* ###
* 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.features.codecompare.functiongraph;
import java.awt.Component;
import docking.ComponentProvider;
import ghidra.features.base.codecompare.panel.CodeComparisonActionContext;
import ghidra.features.base.codecompare.panel.CodeComparisonView;
/**
* Action context for a dual Function Graph panel.
*/
public class FgComparisonContext extends CodeComparisonActionContext {
private FunctionGraphCodeComparisonView fgProvider;
private FgDisplay display;
private boolean isLeft;
public FgComparisonContext(ComponentProvider provider,
FunctionGraphCodeComparisonView fgPanel,
FgDisplay display, Component component, boolean isLeft) {
super(provider, fgPanel, component);
this.fgProvider = fgPanel;
this.display = display;
this.isLeft = isLeft;
}
@Override
public CodeComparisonView getCodeComparisonView() {
return fgProvider;
}
public FgDisplay getDisplay() {
return display;
}
public boolean isLeft() {
return isLeft;
}
}

View file

@ -0,0 +1,535 @@
/* ###
* 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.features.codecompare.functiongraph;
import java.util.*;
import java.util.function.Consumer;
import javax.swing.Icon;
import javax.swing.JComponent;
import docking.action.DockingAction;
import docking.tool.ToolConstants;
import ghidra.app.nav.*;
import ghidra.app.plugin.core.colorizer.ColorizingService;
import ghidra.app.plugin.core.functiongraph.*;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutOptions;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
import ghidra.app.plugin.core.functiongraph.graph.layout.flowchart.FlowChartLayoutProvider;
import ghidra.app.plugin.core.functiongraph.mvc.*;
import ghidra.app.services.ClipboardService;
import ghidra.app.util.ListingHighlightProvider;
import ghidra.app.util.viewer.format.FormatManager;
import ghidra.framework.model.DomainObjectChangedEvent;
import ghidra.framework.model.DomainObjectListener;
import ghidra.framework.options.*;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.ServiceListener;
import ghidra.graph.viewer.options.VisualGraphOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.UniversalIdGenerator;
import ghidra.util.exception.AssertException;
import ghidra.util.task.SwingUpdateManager;
/**
* This class displays a Function Graph in the left or right side of the Function Comparison view.
*/
public class FgDisplay implements OptionsChangeListener {
private static final String FUNCTION_GRAPH_NAME = "Function Graph";
private PluginTool tool;
private String owner;
private Program program;
private FGController controller;
private FgOptions fgOptions;
private FormatManager userDefinedFormatManager;
private ProgramLocation currentLocation;
private FgDisplayProgramListener programListener = new FgDisplayProgramListener();
private FgServiceListener serviceListener = new FgServiceListener();
private FGColorProvider colorProvider;
private List<FGLayoutProvider> layoutProviders;
private Consumer<ProgramLocation> locationConsumer;
private Consumer<FgDisplay> graphChangedConsumer;
// Note: this class should probably be using code block highlights and not the code-level
// highlights already provided by the Listing.
// FgHighlighter highlighter;
FgDisplay(FunctionGraphCodeComparisonView fgView,
Consumer<ProgramLocation> locationConsumer, Consumer<FgDisplay> graphChangedConsumer) {
this.tool = fgView.getTool();
this.owner = fgView.getOwner();
this.locationConsumer = locationConsumer;
this.graphChangedConsumer = graphChangedConsumer;
fgOptions = new FgOptions();
layoutProviders = loadLayoutProviders();
colorProvider = new IndependentColorProvider(tool);
init();
FgComparisonEnv env = new FgComparisonEnv();
FGControllerListener listener = new FgCoparisonControllerListener();
controller = new FGController(env, listener);
setDefaultLayout();
}
private void setDefaultLayout() {
FGLayoutProvider initialLayout = layoutProviders.get(0);
for (FGLayoutProvider layout : layoutProviders) {
if (layout.getClass().equals(FlowChartLayoutProvider.class)) {
initialLayout = layout;
break;
}
}
controller.changeLayout(initialLayout);
}
private void init() {
tool.addServiceListener(serviceListener);
ColorizingService colorizingService = tool.getService(ColorizingService.class);
if (colorizingService != null) {
colorProvider = new ToolBasedColorProvider(() -> program, colorizingService);
}
}
private List<FGLayoutProvider> loadLayoutProviders() {
// Shared Code Note: This code is mirrored in the FgDisplay for the Code Comparison API
FGLayoutFinder layoutFinder = new DiscoverableFGLayoutFinder();
List<FGLayoutProvider> instances = layoutFinder.findLayouts();
if (instances.isEmpty()) {
throw new AssertException("Could not find any layout providers. You project may not " +
"be configured properly.");
}
List<FGLayoutProvider> layouts = new ArrayList<>(instances);
Collections.sort(layouts, (o1, o2) -> -o1.getPriorityLevel() + o2.getPriorityLevel());
return layouts;
}
private void initializeOptions() {
ToolOptions options = tool.getOptions(ToolConstants.GRAPH_OPTIONS);
options.removeOptionsChangeListener(this);
options.addOptionsChangeListener(this);
// Graph -> Function Graph
Options subOptions = options.getOptions(FUNCTION_GRAPH_NAME);
fgOptions.registerOptions(subOptions);
fgOptions.loadOptions(subOptions);
for (FGLayoutProvider layoutProvider : layoutProviders) {
// Graph -> Function Graph -> Layout Name
String layoutName = layoutProvider.getLayoutName();
Options layoutToolOptions = subOptions.getOptions(layoutName);
FGLayoutOptions layoutOptions = layoutProvider.createLayoutOptions(layoutToolOptions);
if (layoutOptions == null) {
continue; // many layouts do not have options
}
layoutOptions.registerOptions(layoutToolOptions);
layoutOptions.loadOptions(layoutToolOptions);
fgOptions.setLayoutOptions(layoutName, layoutOptions);
}
}
public FGController getController() {
return controller;
}
public String getOwner() {
return owner;
}
public JComponent getComponent() {
return controller.getViewComponent();
}
public void dispose() {
if (program != null) {
program.removeListener(programListener);
program = null;
}
programListener.dispose();
controller.cleanup();
}
@Override
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
Object newValue) {
// Graph -> Function Graph
Options subOptions = options.getOptions(FUNCTION_GRAPH_NAME);
fgOptions.loadOptions(subOptions);
controller.optionsChanged();
if (fgOptions.optionChangeRequiresRelayout(optionName)) {
controller.refresh(true);
}
else if (VisualGraphOptions.VIEW_RESTORE_OPTIONS_KEY.equals(optionName)) {
controller.clearViewSettings();
}
else {
controller.refreshDisplayWithoutRebuilding();
}
}
public void showFunction(Function function) {
updateProgram(function);
if (function == null) {
controller.setStatusMessage("No Function");
return;
}
if (function.isExternal()) {
String name = function.getName(true);
controller.setStatusMessage("\"" + name + "\" is an external function.");
return;
}
Address entry = function.getEntryPoint();
currentLocation = new ProgramLocation(program, entry);
controller.display(program, currentLocation);
}
public void setLocation(ProgramLocation location) {
controller.setLocation(location);
}
public ProgramLocation getLocation() {
return controller.getLocation();
}
private void updateProgram(Function function) {
Program newProgram = function == null ? null : function.getProgram();
if (program == newProgram) {
return;
}
if (program != null) {
program.removeListener(programListener);
}
program = newProgram;
if (program != null) {
program.addListener(programListener);
initializeOptions();
}
}
public boolean isBusy() {
return controller.isBusy();
}
private void refresh() {
controller.refresh(true);
}
private class FgComparisonEnv implements FgEnv {
private Navigatable navigatable = new DummyNavigatable();
@Override
public PluginTool getTool() {
return tool;
}
@Override
public Program getProgram() {
return program;
}
@Override
public FunctionGraphOptions getOptions() {
return fgOptions;
}
@Override
public List<FGLayoutProvider> getLayoutProviders() {
return layoutProviders;
}
@Override
public void addLocalAction(DockingAction action) {
// stub
}
@Override
public FGColorProvider getColorProvider() {
return colorProvider;
}
@Override
public FormatManager getUserDefinedFormat() {
return userDefinedFormatManager;
}
@Override
public void setUserDefinedFormat(FormatManager format) {
userDefinedFormatManager = format;
}
@Override
public Navigatable getNavigatable() {
return navigatable;
}
@Override
public ProgramLocation getToolLocation() {
// this isn't really the tool's location, but maybe this is fine for this display
return currentLocation;
}
@Override
public ProgramLocation getGraphLocation() {
return currentLocation;
}
@Override
public void setSelection(ProgramSelection selection) {
controller.setSelection(selection);
}
}
private class FgCoparisonControllerListener implements FGControllerListener {
@Override
public void dataChanged() {
graphChangedConsumer.accept(FgDisplay.this);
}
@Override
public void userChangedLocation(ProgramLocation location, boolean vertexChanged) {
currentLocation = location;
locationConsumer.accept(location);
}
@Override
public void userChangedSelection(ProgramSelection selection) {
// stub
}
@Override
public void userSelectedText(String s) {
// stub
}
@Override
public void userNavigated(ProgramLocation location) {
// stub
}
}
private class DummyNavigatable implements Navigatable {
private long id;
DummyNavigatable() {
id = UniversalIdGenerator.nextID().getValue();
}
@Override
public long getInstanceID() {
return id;
}
@Override
public boolean goTo(Program p, ProgramLocation pl) {
return false;
}
@Override
public ProgramLocation getLocation() {
return null;
}
@Override
public Program getProgram() {
return program;
}
@Override
public LocationMemento getMemento() {
return new FgMemento(); // dummy
}
@Override
public void setMemento(LocationMemento memento) {
// stub
}
@Override
public Icon getNavigatableIcon() {
return null;
}
@Override
public boolean isConnected() {
return false;
}
@Override
public boolean supportsMarkers() {
return false;
}
@Override
public void requestFocus() {
// stub
}
@Override
public boolean isVisible() {
return true;
}
@Override
public void setSelection(ProgramSelection selection) {
// stub
}
@Override
public void setHighlight(ProgramSelection highlight) {
// stub
}
@Override
public ProgramSelection getSelection() {
return null;
}
@Override
public ProgramSelection getHighlight() {
return null;
}
@Override
public String getTextSelection() {
return null;
}
@Override
public void addNavigatableListener(NavigatableRemovalListener listener) {
// stub
}
@Override
public void removeNavigatableListener(NavigatableRemovalListener listener) {
// stub
}
@Override
public boolean isDisposed() {
return false;
}
@Override
public boolean supportsHighlight() {
return false;
}
@Override
public void setHighlightProvider(ListingHighlightProvider highlightProvider,
Program program) {
// stub
}
@Override
public void removeHighlightProvider(ListingHighlightProvider highlightProvider,
Program p) {
// stub
}
}
private class FgMemento extends LocationMemento {
FgMemento() {
super((Program) null, null);
}
}
private class FgDisplayProgramListener implements DomainObjectListener {
private SwingUpdateManager updater = new SwingUpdateManager(500, 5000, () -> refresh());
@Override
public void domainObjectChanged(DomainObjectChangedEvent ev) {
updater.update();
}
public void dispose() {
updater.dispose();
}
}
private class FgServiceListener implements ServiceListener {
@Override
public void serviceAdded(Class<?> interfaceClass, Object service) {
if (interfaceClass == ClipboardService.class) {
// if we decide to support copy/paste in this viewer, then the FGClipboardProvider
// needs to be taken out of the FGProvider and made independent. We would also need
// to refactor the ClipboardPlugin to not need a provider, instead adding a way to
// get a component, context and to add/remove actions.
}
else if (interfaceClass == ColorizingService.class) {
colorProvider =
new ToolBasedColorProvider(() -> program, (ColorizingService) service);
controller.refresh(true);
}
}
@Override
public void serviceRemoved(Class<?> interfaceClass, Object service) {
if (interfaceClass == ColorizingService.class) {
colorProvider = new IndependentColorProvider(tool);
controller.refresh(true);
}
}
}
private class FgOptions extends FunctionGraphOptions {
@Override
public void setUseAnimation(boolean useAnimation) {
// don't allow this to be changed; animations seem like overkill for comparisons
}
@Override
public void loadOptions(Options options) {
super.loadOptions(options);
useAnimation = false;
}
}
}

View file

@ -0,0 +1,57 @@
/* ###
* 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.features.codecompare.functiongraph;
import ghidra.app.util.viewer.listingpanel.ProgramLocationTranslator;
import ghidra.program.util.ListingAddressCorrelation;
import ghidra.program.util.ProgramLocation;
import ghidra.util.datastruct.Duo;
import ghidra.util.datastruct.Duo.Side;
/**
* A class to synchronize locations between the left and right Function Graph comparison panels.
*/
class FgDisplaySynchronizer {
private Duo<FgDisplay> displays;
private ProgramLocationTranslator locationTranslator;
FgDisplaySynchronizer(Duo<FgDisplay> displays, ListingAddressCorrelation correlation) {
this.displays = displays;
this.locationTranslator = new ProgramLocationTranslator(correlation);
}
void setLocation(Side side, ProgramLocation location) {
// Only set other side's cursor if we are coordinating right now.
Side otherSide = side.otherSide();
ProgramLocation otherLocation = locationTranslator.getProgramLocation(otherSide, location);
if (otherLocation != null) {
displays.get(otherSide).setLocation(otherLocation);
}
}
void sync(Side side) {
ProgramLocation programLocation = displays.get(side).getLocation();
if (programLocation != null) {
setLocation(side, programLocation);
}
}
void dispose() {
// this object should probably have a dispose() method
// locationTranslator.dispose();
}
}

View file

@ -0,0 +1,508 @@
/* ###
* 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.features.codecompare.functiongraph;
import static ghidra.util.datastruct.Duo.Side.*;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.function.Consumer;
import javax.swing.JComponent;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.action.DockingAction;
import ghidra.GhidraOptions;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
import ghidra.app.plugin.core.functiongraph.mvc.*;
import ghidra.app.util.viewer.format.FormatManager;
import ghidra.features.base.codecompare.listing.LinearAddressCorrelation;
import ghidra.features.base.codecompare.panel.CodeComparisonView;
import ghidra.features.codecompare.functiongraph.actions.*;
import ghidra.framework.options.SaveState;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginTool;
import ghidra.graph.viewer.GraphSatelliteListener;
import ghidra.program.model.correlate.HashedFunctionAddressCorrelation;
import ghidra.program.model.listing.Function;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.util.ListingAddressCorrelation;
import ghidra.program.util.ProgramLocation;
import ghidra.util.HelpLocation;
import ghidra.util.datastruct.Duo;
import ghidra.util.datastruct.Duo.Side;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import help.Help;
/**
* Provides a {@link CodeComparisonView} for displaying function graphs.
* <P>
* Known Issues:
* <UL>
* <LI>The options used by this API are the same as those for the Function Graph plugin. We
* may find that some of the options should not apply to this API. If true, then we would
* have to create a new options entry in the tool and a different options object for this
* API to use.
* </LI>
* <LI>Each open panel will potentially update the state that is saved in the tool. This can
* lead to confusion when multiple open windows have different settings, as it is not
* clear which window's settings will get saved.
* </LI>
* <LI>The views do not support copying, which is consistent with the other function comparison
* views.
* </LI>
* </UL>
*/
public class FunctionGraphCodeComparisonView extends CodeComparisonView {
public static final String NAME = "Function Graph View";
private static final String FORMAT_KEY = "FIELD_FORMAT";
private static final String SHOW_POPUPS_KEY = "SHOW_POPUPS";
private static final String SHOW_SATELLITE_KEY = "SHOW_SATELLITE";
private static final String LAYOUT_NAME = "LAYOUT_NAME";
private static final String COMPLEX_LAYOUT_NAME = "COMPLEX_LAYOUT_NAME";
private static final String LAYOUT_CLASS_NAME = "LAYOUT_CLASS_NAME";
private FgDisplaySynchronizer coordinator;
private Duo<FgDisplay> displays = new Duo<>();
private Duo<Function> functions = new Duo<>();
private ListingAddressCorrelation addressCorrelator;
private boolean displaysLocked;
private SaveState defaultSaveState;
private SaveState saveState;
private List<DockingAction> actions = new ArrayList<>();
private FgTogglePopupsAction showPopupsAction;
private FgToggleSatelliteAction showSatelliteAction;
public FunctionGraphCodeComparisonView(String owner, PluginTool tool) {
super(owner, tool);
Help.getHelpService()
.registerHelp(this, new HelpLocation(HELP_TOPIC, "FunctionGraph_Diff_View"));
displays = buildDisplays();
createActions();
installSatelliteListeners();
buildDefaultSaveState();
buildPanel();
setSynchronizedScrolling(true);
}
public String getOwner() {
return owner;
}
public Duo<FgDisplay> getDisplays() {
return displays;
}
/**
* Called by actions to signal that the user changed something worth saving.
*/
public void stateChanged() {
saveShowPopups(saveState);
saveShowSatellite(saveState);
saveLayout(saveState);
saveCustomFormat(saveState);
if (!hasStateChanges()) {
// This implies the user has made changes, but those changes match the default settings.
// Clear the save state so no changes get written to the tool.
saveState.clear();
}
tool.setConfigChanged(true);
}
private void buildDefaultSaveState() {
SaveState ss = new SaveState();
saveShowPopups(ss);
saveLayout(ss);
saveCustomFormat(ss);
defaultSaveState = ss;
}
private void saveShowPopups(SaveState ss) {
ss.putBoolean(SHOW_POPUPS_KEY, showPopupsAction.isSelected());
}
private void saveShowSatellite(SaveState ss) {
ss.putBoolean(SHOW_SATELLITE_KEY, showSatelliteAction.isSelected());
}
private void loadShowPopups(SaveState ss) {
FgDisplay leftDisplay = displays.get(LEFT);
FGController leftController = leftDisplay.getController();
boolean currentShowPopups = leftController.arePopupsVisible();
boolean savedShowPopups = ss.getBoolean(SHOW_POPUPS_KEY, currentShowPopups);
if (currentShowPopups == savedShowPopups) {
return;
}
FgDisplay rightDisplay = displays.get(Side.RIGHT);
FGController rightController = rightDisplay.getController();
leftController.setPopupsVisible(savedShowPopups);
rightController.setPopupsVisible(savedShowPopups);
showPopupsAction.setSelected(savedShowPopups);
}
private void loadShowSatellite(SaveState ss) {
FgDisplay leftDisplay = displays.get(LEFT);
FGController leftController = leftDisplay.getController();
boolean currentShowSatellite = leftController.isSatelliteVisible();
boolean savedShowSatellite = ss.getBoolean(SHOW_SATELLITE_KEY, currentShowSatellite);
if (currentShowSatellite == savedShowSatellite) {
return;
}
FgDisplay rightDisplay = displays.get(Side.RIGHT);
FGController rightController = rightDisplay.getController();
leftController.setSatelliteVisible(savedShowSatellite);
rightController.setSatelliteVisible(savedShowSatellite);
showSatelliteAction.setSelected(savedShowSatellite);
}
private void saveLayout(SaveState ss) {
FgDisplay display = displays.get(LEFT);
FGController controller = display.getController();
FGLayoutProvider layout = controller.getLayoutProvider();
SaveState layoutState = new SaveState(COMPLEX_LAYOUT_NAME);
String layoutName = layout.getLayoutName();
layoutState.putString(LAYOUT_NAME, layoutName);
layoutState.putString(LAYOUT_CLASS_NAME, layout.getClass().getName());
ss.putSaveState(COMPLEX_LAYOUT_NAME, layoutState);
}
private void loadLayout(SaveState ss) {
SaveState layoutState = saveState.getSaveState(COMPLEX_LAYOUT_NAME);
if (layoutState == null) {
return;
}
FgDisplay leftDisplay = displays.get(LEFT);
FGController leftController = leftDisplay.getController();
FGLayoutProvider layout = leftController.getLayoutProvider();
String savedLayoutName = layoutState.getString(LAYOUT_NAME, layout.getLayoutName());
if (layout.getLayoutName().equals(savedLayoutName)) {
return; // already set
}
FgDisplay rightDisplay = displays.get(Side.RIGHT);
FGController rightController = rightDisplay.getController();
FgEnv env = leftController.getEnv();
List<FGLayoutProvider> layoutProviders = new ArrayList<>(env.getLayoutProviders());
for (FGLayoutProvider layoutProvider : layoutProviders) {
String providerName = layoutProvider.getLayoutName();
if (providerName.equals(savedLayoutName)) {
leftController.changeLayout(layoutProvider);
rightController.changeLayout(layoutProvider);
break;
}
}
}
private void saveCustomFormat(SaveState ss) {
FgDisplay display = displays.get(LEFT);
FGController controller = display.getController();
FormatManager format = controller.getMinimalFormatManager();
SaveState formatState = new SaveState();
format.saveState(formatState);
ss.putSaveState(FORMAT_KEY, formatState);
}
private void loadCustomFormat(SaveState ss) {
SaveState formatState = ss.getSaveState(FORMAT_KEY);
if (formatState == null) {
return;
}
ToolOptions displayOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
FgDisplay leftDisplay = displays.get(LEFT);
FGController leftController = leftDisplay.getController();
FormatManager format = leftController.getMinimalFormatManager();
SaveState testState = new SaveState();
format.saveState(testState);
if (equals(testState, formatState)) {
return;
}
FormatManager formatManager = new FormatManager(displayOptions, fieldOptions);
formatManager.readState(formatState);
leftController.updateMinimalFormatManager(formatManager);
FgDisplay rightDisplay = displays.get(Side.RIGHT);
FGController rightController = rightDisplay.getController();
rightController.updateMinimalFormatManager(formatManager);
}
@Override
public void setSaveState(SaveState ss) {
this.saveState = ss;
if (!hasStateChanges()) {
return; // the given state matches the default state; nothing to do
}
loadShowPopups(ss);
loadShowSatellite(ss);
loadLayout(ss);
loadCustomFormat(ss);
}
private boolean hasStateChanges() {
if (!saveState.isEmpty()) {
return !equals(saveState, defaultSaveState);
}
return false;
}
private boolean equals(SaveState state1, SaveState state2) {
String s1 = state1.toString();
String s2 = state2.toString();
return Objects.equals(s1, s2);
}
private void createActions() {
// Note: many of these actions are similar to what is in the main Function Graph. The way
// this class is coded, the actions do not share keybindings. This is something we may wish
// to change in the future by making the key binding type sharable.
// Both displays have the same actions they get from the Function Graph API. We will add
// them to the comparison provider. We only need to add one set of actions, since they are
// the same for both providers.
FgDisplay left = displays.get(LEFT);
actions.add(new FgResetGraphAction(left));
FgDisplay right = displays.get(RIGHT);
actions.add(new FgResetGraphAction(right));
showPopupsAction = new FgTogglePopupsAction(this);
FGController controller = left.getController();
boolean showPopups = controller.arePopupsVisible();
showPopupsAction.setSelected(showPopups);
showSatelliteAction = new FgToggleSatelliteAction(this);
boolean showSatellite = controller.isSatelliteVisible();
showSatelliteAction.setSelected(showSatellite);
actions.add(showSatelliteAction);
actions.add(showPopupsAction);
actions.add(new FgRelayoutAction(this));
actions.add(new FgChooseFormatAction(this));
}
private void installSatelliteListeners() {
FgDisplay left = displays.get(LEFT);
FgDisplay right = displays.get(RIGHT);
FGController leftController = left.getController();
FGController rightController = right.getController();
GraphSatelliteListener listener = new GraphSatelliteListener() {
@Override
public void satelliteVisibilityChanged(boolean docked, boolean visible) {
if (visible) {
leftController.setSatelliteVisible(true);
rightController.setSatelliteVisible(true);
}
showSatelliteAction.setSelected(visible);
stateChanged();
}
};
FGView lv = leftController.getView();
FGView rv = rightController.getView();
lv.addSatelliteListener(listener);
rv.addSatelliteListener(listener);
}
@Override
public List<DockingAction> getActions() {
List<DockingAction> superActions = super.getActions();
superActions.addAll(0, actions);
return actions;
}
@Override
protected void comparisonDataChanged() {
maybeLoadFunction(LEFT, comparisonData.get(LEFT).getFunction());
maybeLoadFunction(RIGHT, comparisonData.get(RIGHT).getFunction());
addressCorrelator = createCorrelator();
// updateProgramViews();
updateCoordinator();
//initializeCursorMarkers();
updateActionEnablement();
validate();
}
private ListingAddressCorrelation createCorrelator() {
Function f1 = getFunction(LEFT);
Function f2 = getFunction(RIGHT);
if (f1 != null && f2 != null) {
try {
return new HashedFunctionAddressCorrelation(f1, f2, TaskMonitor.DUMMY);
}
catch (CancelledException | MemoryAccessException e) {
// fall back to linear address correlation
}
}
if (comparisonData.get(LEFT).isEmpty() || comparisonData.get(RIGHT).isEmpty()) {
return null;
}
return new LinearAddressCorrelation(comparisonData);
}
private void updateCoordinator() {
if (coordinator != null) {
coordinator.dispose();
coordinator = null;
}
if (displaysLocked) {
coordinator = new FgDisplaySynchronizer(displays, addressCorrelator);
coordinator.sync(activeSide);
}
}
private void maybeLoadFunction(Side side, Function function) {
// we keep a local copy of the function so we know if it is already decompiled
if (functions.get(side) == function) {
return;
}
// Clear the scroll info and highlight info to prevent unnecessary highlighting, etc.
loadFunction(side, null);
loadFunction(side, function);
}
private void loadFunction(Side side, Function function) {
if (functions.get(side) != function) {
functions = functions.with(side, function);
displays.get(side).showFunction(function);
}
}
private Duo<FgDisplay> buildDisplays() {
// The function graph's display is not ready until a graph is loaded. It also gets rebuilt
// each time the graph is reloaded. To correctly install listeners, we need to update them
// as the graph is rebuilt.
Consumer<FgDisplay> graphChangedCallback = display -> {
Side side = getSide(display);
addMouseAndFocusListeners(side);
};
FgDisplay leftDisplay =
new FgDisplay(this, l -> locationChanged(LEFT, l), graphChangedCallback);
FgDisplay rightDisplay =
new FgDisplay(this, l -> locationChanged(RIGHT, l), graphChangedCallback);
return new Duo<>(leftDisplay, rightDisplay);
}
private Side getSide(FgDisplay display) {
FgDisplay leftDisplay = displays.get(LEFT);
if (display == leftDisplay) {
return LEFT;
}
return RIGHT;
}
private void locationChanged(Side side, ProgramLocation location) {
if (coordinator != null) {
coordinator.setLocation(side, location);
}
}
@Override
public String getName() {
return NAME;
}
@Override
public void dispose() {
setSynchronizedScrolling(false); // disposes any exiting coordinator
displays.each(FgDisplay::dispose);
}
/**
* Gets the display from the active side.
* @return the active display
*/
public FgDisplay getActiveDisplay() {
return displays.get(activeSide);
}
@Override
public ActionContext getActionContext(ComponentProvider provider, MouseEvent event) {
FgDisplay display = getActiveDisplay();
Component component = event != null ? event.getComponent()
: display.getComponent();
boolean isLeft = activeSide == LEFT;
return new FgComparisonContext(provider, this, display, component, isLeft);
}
@Override
public void updateActionEnablement() {
// stub
}
@Override
public void setSynchronizedScrolling(boolean synchronize) {
if (coordinator != null) {
coordinator.dispose();
coordinator = null;
}
displaysLocked = synchronize;
if (displaysLocked) {
coordinator = new FgDisplaySynchronizer(displays, addressCorrelator);
coordinator.sync(activeSide);
}
}
@Override
public JComponent getComparisonComponent(Side side) {
return displays.get(side).getComponent();
}
public boolean isBusy() {
return displays.get(LEFT).isBusy() || displays.get(RIGHT).isBusy();
}
}

View file

@ -0,0 +1,43 @@
/* ###
* 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.features.codecompare.functiongraph.actions;
import docking.ActionContext;
import docking.action.DockingAction;
import ghidra.features.codecompare.functiongraph.FgComparisonContext;
import ghidra.features.codecompare.functiongraph.FgDisplay;
public abstract class AbstractFgAction extends DockingAction {
private FgDisplay display;
protected AbstractFgAction(FgDisplay display, String name) {
super(name, display.getOwner());
this.display = display;
}
protected boolean isMyDisplay(ActionContext context) {
if (!(context instanceof FgComparisonContext fgContext)) {
return false;
}
return fgContext.getDisplay() == display;
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return isMyDisplay(context);
}
}

View file

@ -0,0 +1,69 @@
/* ###
* 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.features.codecompare.functiongraph.actions;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
import ghidra.app.util.viewer.format.FormatManager;
import ghidra.features.codecompare.functiongraph.*;
import ghidra.util.HelpLocation;
import ghidra.util.datastruct.Duo;
import ghidra.util.datastruct.Duo.Side;
public class FgChooseFormatAction extends DockingAction {
private FunctionGraphCodeComparisonView fgProvider;
public FgChooseFormatAction(FunctionGraphCodeComparisonView fgProvider) {
super("Edit Code Block Fields", fgProvider.getOwner());
this.fgProvider = fgProvider;
setPopupMenuData(new MenuData(new String[] { "Edit Fields" }));
setHelpLocation(
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Format"));
}
@Override
public void actionPerformed(ActionContext context) {
Duo<FgDisplay> displays = fgProvider.getDisplays();
FgDisplay leftDisplay = displays.get(Side.LEFT);
FGController leftController = leftDisplay.getController();
leftController.showFormatChooser();
FormatManager leftFormatManager = leftController.getMinimalFormatManager();
FgDisplay rightDisplay = displays.get(Side.RIGHT);
FGController rightController = rightDisplay.getController();
rightController.updateMinimalFormatManager(leftFormatManager);
fgProvider.stateChanged();
}
@Override
public boolean isEnabledForContext(ActionContext context) {
if (!(context instanceof FgComparisonContext)) {
return false;
}
Duo<FgDisplay> displays = fgProvider.getDisplays();
FgDisplay leftDisplay = displays.get(Side.LEFT);
FGController controller = leftDisplay.getController();
return controller.hasResults();
}
}

View file

@ -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.features.codecompare.functiongraph.actions;
import java.util.ArrayList;
import java.util.List;
import docking.ActionContext;
import docking.action.*;
import docking.widgets.dialogs.ObjectChooserDialog;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
import ghidra.app.plugin.core.functiongraph.mvc.FgEnv;
import ghidra.features.codecompare.functiongraph.*;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.HelpLocation;
import ghidra.util.datastruct.Duo;
import ghidra.util.datastruct.Duo.Side;
public class FgRelayoutAction extends DockingAction {
private FunctionGraphCodeComparisonView fgProvider;
public FgRelayoutAction(FunctionGraphCodeComparisonView fgProvider) {
super("Relayout Graph", fgProvider.getOwner(), KeyBindingType.SHARED);
this.fgProvider = fgProvider;
setPopupMenuData(new MenuData(new String[] { "Relayout Graph" }));
setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Layout"));
}
@Override
public void actionPerformed(ActionContext context) {
Duo<FgDisplay> displays = fgProvider.getDisplays();
FgDisplay leftDisplay = displays.get(Side.LEFT);
FGController leftController = leftDisplay.getController();
FgEnv env = leftController.getEnv();
List<FGLayoutProvider> layoutProviders = new ArrayList<>(env.getLayoutProviders());
ObjectChooserDialog<FGLayoutProvider> dialog =
new ObjectChooserDialog<>("Choose Layout", FGLayoutProvider.class, layoutProviders,
"getLayoutName");
FGLayoutProvider currentLayout = leftController.getLayoutProvider();
dialog.setSelectedObject(currentLayout);
PluginTool tool = env.getTool();
tool.showDialog(dialog);
FGLayoutProvider layoutProvider = dialog.getSelectedObject();
if (layoutProvider == null) {
return; // cancelled
}
leftController.changeLayout(layoutProvider);
FgDisplay rightDisplay = displays.get(Side.RIGHT);
FGController rightController = rightDisplay.getController();
rightController.changeLayout(layoutProvider);
fgProvider.stateChanged();
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return context instanceof FgComparisonContext;
}
}

View file

@ -0,0 +1,62 @@
/* ###
* 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.features.codecompare.functiongraph.actions;
import javax.swing.JComponent;
import docking.ActionContext;
import docking.action.MenuData;
import docking.widgets.OptionDialog;
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
import ghidra.features.codecompare.functiongraph.FgDisplay;
import ghidra.util.HelpLocation;
public class FgResetGraphAction extends AbstractFgAction {
private FgDisplay display;
public FgResetGraphAction(FgDisplay display) {
super(display, "Reset Graph");
this.display = display;
setPopupMenuData(new MenuData(new String[] { "Reset Graph" }));
setHelpLocation(
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Reload_Graph"));
}
@Override
public void actionPerformed(ActionContext context) {
FGController controller = display.getController();
JComponent component = controller.getViewComponent();
int choice = OptionDialog.showYesNoDialog(component, "Reset Graph?",
"<html>Erase all vertex position and grouping information?");
if (choice != OptionDialog.YES_OPTION) {
return;
}
controller.resetGraph();
}
@Override
public boolean isEnabledForContext(ActionContext context) {
if (!super.isEnabledForContext(context)) {
return false;
}
FGController controller = display.getController();
return controller.hasResults();
}
}

View file

@ -0,0 +1,71 @@
/* ###
* 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.features.codecompare.functiongraph.actions;
import docking.ActionContext;
import docking.action.MenuData;
import docking.action.ToggleDockingAction;
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
import ghidra.features.codecompare.functiongraph.*;
import ghidra.util.HelpLocation;
import ghidra.util.datastruct.Duo;
import ghidra.util.datastruct.Duo.Side;
/**
* An action to toggle popup enablement for the Function Graph comparison views.
*/
public class FgTogglePopupsAction extends ToggleDockingAction {
private FunctionGraphCodeComparisonView fgProvider;
public FgTogglePopupsAction(FunctionGraphCodeComparisonView fgProvider) {
super("Display Popup Windows", fgProvider.getOwner());
this.fgProvider = fgProvider;
setPopupMenuData(new MenuData(new String[] { "Display Popup Windows" }));
setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Popups"));
}
@Override
public void actionPerformed(ActionContext context) {
Duo<FgDisplay> displays = fgProvider.getDisplays();
FgDisplay leftDisplay = displays.get(Side.LEFT);
FGController controller = leftDisplay.getController();
boolean visible = isSelected();
controller.setPopupsVisible(visible);
FgDisplay rightDisplay = displays.get(Side.RIGHT);
FGController rightController = rightDisplay.getController();
rightController.setPopupsVisible(visible);
fgProvider.stateChanged();
}
@Override
public boolean isEnabledForContext(ActionContext context) {
if (!(context instanceof FgComparisonContext)) {
return false;
}
Duo<FgDisplay> displays = fgProvider.getDisplays();
FgDisplay leftDisplay = displays.get(Side.LEFT);
FGController controller = leftDisplay.getController();
return controller.hasResults();
}
}

Some files were not shown because too many files have changed in this diff Show more