diff --git a/Ghidra/Features/PyGhidra/.gitignore b/Ghidra/Features/PyGhidra/.gitignore index adc16ce4d2..8c59df5a21 100644 --- a/Ghidra/Features/PyGhidra/.gitignore +++ b/Ghidra/Features/PyGhidra/.gitignore @@ -1 +1,2 @@ /.pytest_cache/ +!.pydevproject diff --git a/Ghidra/Features/PyGhidra/.pydevproject b/Ghidra/Features/PyGhidra/.pydevproject new file mode 100644 index 0000000000..72a29a46c5 --- /dev/null +++ b/Ghidra/Features/PyGhidra/.pydevproject @@ -0,0 +1,12 @@ + + + + Default + + python interpreter + + + /${PROJECT_DIR_NAME}/ghidra_scripts + /${PROJECT_DIR_NAME}/src/main/py/src/pyghidra + + diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/__init__.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/__init__.py index 906dbb3bf8..58b01b5552 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/__init__.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/__init__.py @@ -54,10 +54,10 @@ debug_callback = _debug_callback # Expose API -from .core import run_script, start, started, open_program -from .launcher import DeferredPyGhidraLauncher, GuiPyGhidraLauncher, HeadlessPyGhidraLauncher -from .script import get_current_interpreter -from .version import ApplicationInfo, ExtensionDetails +from pyghidra.core import run_script, start, started, open_program +from pyghidra.launcher import DeferredPyGhidraLauncher, GuiPyGhidraLauncher, HeadlessPyGhidraLauncher +from pyghidra.script import get_current_interpreter +from pyghidra.version import ApplicationInfo, ExtensionDetails __all__ = [ diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/__main__.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/__main__.py index aff024147e..1d6fe812e8 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/__main__.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/__main__.py @@ -20,7 +20,6 @@ import logging import sys from pathlib import Path -import pyghidra import pyghidra.core import pyghidra.gui diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/core.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/core.py index 2a3faa8e96..a6c208a52d 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/core.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/core.py @@ -14,7 +14,6 @@ # limitations under the License. ## import contextlib -from pathlib import Path from typing import Union, TYPE_CHECKING, Tuple, ContextManager, List, Optional from pyghidra.converters import * # pylint: disable=wildcard-import, unused-wildcard-import @@ -51,29 +50,29 @@ def started() -> bool: return PyGhidraLauncher.has_launched() -def _get_language(id: str) -> "Language": +def _get_language(lang_id: str) -> "Language": from ghidra.program.util import DefaultLanguageService from ghidra.program.model.lang import LanguageID, LanguageNotFoundException try: service: "LanguageService" = DefaultLanguageService.getLanguageService() - return service.getLanguage(LanguageID(id)) + return service.getLanguage(LanguageID(lang_id)) except LanguageNotFoundException: # suppress the java exception pass - raise ValueError("Invalid Language ID: "+id) + raise ValueError("Invalid Language ID: " + lang_id) -def _get_compiler_spec(lang: "Language", id: str = None) -> "CompilerSpec": - if id is None: +def _get_compiler_spec(lang: "Language", compiler: str = None) -> "CompilerSpec": + if compiler is None: return lang.getDefaultCompilerSpec() from ghidra.program.model.lang import CompilerSpecID, CompilerSpecNotFoundException try: - return lang.getCompilerSpecByID(CompilerSpecID(id)) + return lang.getCompilerSpecByID(CompilerSpecID(compiler)) except CompilerSpecNotFoundException: # suppress the java exception pass lang_id = lang.getLanguageID() - raise ValueError(f"Invalid CompilerSpecID: {id} for Language: {lang_id.toString()}") + raise ValueError(f"Invalid CompilerSpecID: {compiler} for Language: {lang_id.toString()}") def _setup_project( @@ -85,8 +84,8 @@ def _setup_project( loader: Union[str, JClass] = None ) -> Tuple["GhidraProject", "Program"]: from ghidra.base.project import GhidraProject - from java.lang import ClassLoader - from java.io import IOException + from java.lang import ClassLoader # type:ignore @UnresolvedImport + from java.io import IOException # type:ignore @UnresolvedImport if binary_path is not None: binary_path = Path(binary_path) if project_location: @@ -99,7 +98,7 @@ def _setup_project( project_location.mkdir(exist_ok=True, parents=True) if isinstance(loader, str): - from java.lang import ClassNotFoundException + from java.lang import ClassNotFoundException # type:ignore @UnresolvedImport try: gcl = ClassLoader.getSystemClassLoader() loader = JClass(loader, gcl) @@ -108,7 +107,8 @@ def _setup_project( if isinstance(loader, JClass): from ghidra.app.util.opinion import Loader - if not Loader.class_.isAssignableFrom(loader): + loader_cls = Loader.class_ + if not loader_cls.isAssignableFrom(loader): raise TypeError(f"{loader} does not implement ghidra.app.util.opinion.Loader") # Open/Create project @@ -157,8 +157,8 @@ def _setup_script(project: "GhidraProject", program: "Program"): from ghidra.program.util import ProgramLocation from ghidra.util.task import TaskMonitor - from java.io import PrintWriter - from java.lang import System + from java.io import PrintWriter # type:ignore @UnresolvedImport + from java.lang import System # type:ignore @UnresolvedImport if project is not None: project = project.getProject() @@ -185,7 +185,7 @@ def _analyze_program(flat_api, program): if hasattr(GhidraProgramUtilities, "markProgramAnalyzed"): GhidraProgramUtilities.markProgramAnalyzed(program) else: - GhidraProgramUtilities.setAnalyzedFlag(program, True) + GhidraProgramUtilities.setAnalyzedFlag(program, True) # @UndefinedVariable finally: GhidraScriptUtil.releaseBundleHostReference() diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/ghidra_launch.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/ghidra_launch.py index 816f79aa34..3827faf766 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/ghidra_launch.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/ghidra_launch.py @@ -19,7 +19,7 @@ from pathlib import Path import sys import threading -from .launcher import PyGhidraLauncher, _run_mac_app +from pyghidra.launcher import PyGhidraLauncher, _run_mac_app class GhidraLauncher(PyGhidraLauncher): @@ -31,12 +31,12 @@ class GhidraLauncher(PyGhidraLauncher): def _launch(self): from ghidra import Ghidra - from java.lang import Runtime, Thread + from java.lang import Runtime, Thread # type:ignore @UnresolvedImport if self._gui: if sys.platform == "win32": appid = ctypes.c_wchar_p(self.app_info.name) - ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appid) + ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appid) # @UndefinedVariable Thread(lambda: Ghidra.main([self._class_name, *self.args])).start() is_exiting = threading.Event() Runtime.getRuntime().addShutdownHook(Thread(is_exiting.set)) diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/ghidradoc.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/ghidradoc.py index 1a64a82981..1423309d6f 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/ghidradoc.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/ghidradoc.py @@ -21,8 +21,8 @@ import json from pathlib import Path import zipfile -from java.lang import Class -from java.io import PrintWriter +from java.lang import Class # type:ignore @UnresolvedImport +from java.io import PrintWriter # type:ignore @UnresolvedImport from jpype import JMethod, JObject, JClass from ghidra.framework import Application @@ -85,7 +85,7 @@ class _Helper: with zipfile.ZipFile(javadoc_zip, "r") as docs: with docs.open(json_path) as f: jsondoc = json.load(f) - except (IOError, KeyError) as e: + except (IOError, KeyError): pass return jsondoc diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/gui.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/gui.py index 186b8b24af..517c1d46bf 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/gui.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/gui.py @@ -112,12 +112,12 @@ def _gui(): # where tkinter can't be imported. Since there may not be an attached # terminal, the problem still needs to be reported somehow. try: - import tkinter.messagebox as _ + import tkinter.messagebox as _ # @UnusedImport except ImportError as e: if platform.system() == "Windows": # there is no console/terminal to report the error import ctypes - MessageBox = ctypes.windll.user32.MessageBoxW + MessageBox = ctypes.windll.user32.MessageBoxW # @UndefinedVariable MessageBox(None, str(e), "Import Error", 0) sys.exit(1) # report this before detaching from the console or no diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/internal/plugin/completions.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/internal/plugin/completions.py index 164a8e5f2d..aa9f0710d6 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/internal/plugin/completions.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/internal/plugin/completions.py @@ -22,7 +22,7 @@ from types import CodeType, FunctionType, MappingProxyType, MethodType, ModuleTy from docking.widgets.label import GLabel from generic.theme import GColor from ghidra.app.plugin.core.console import CodeCompletion -from java.util import Arrays, Collections +from java.util import Arrays, Collections # type:ignore @UnresolvedImport from jpype import JPackage from jpype.types import JDouble, JFloat, JInt, JLong, JShort @@ -80,9 +80,10 @@ class PythonCodeCompleter(Completer): if attr is PythonCodeCompleter._BUILTIN_ATTRIBUTE: if iskeyword(match.rstrip()): return label - attr = builtins.__dict__.get(match, PythonCodeCompleter._BUILTIN_ATTRIBUTE) + builtins_dict = builtins.__dict__ + attr = builtins_dict.get(match, PythonCodeCompleter._BUILTIN_ATTRIBUTE) if attr is not PythonCodeCompleter._BUILTIN_ATTRIBUTE and not match.startswith("__"): - attr = builtins.__dict__[match] + attr = builtins_dict[match] else: return label color = _TYPE_COLORS.get(type(attr), PythonCodeCompleter._BUILTIN_ATTRIBUTE) diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/internal/plugin/plugin.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/internal/plugin/plugin.py index 9c21f086b6..44bb9e8207 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/internal/plugin/plugin.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/internal/plugin/plugin.py @@ -26,11 +26,11 @@ from code import InteractiveConsole from ghidra.framework import Application from ghidra.pyghidra import PyGhidraScriptProvider, PyGhidraPlugin from ghidra.pyghidra.interpreter import PyGhidraConsole -from java.io import BufferedReader, InputStreamReader -from java.lang import String -from java.lang import Thread as JThread -from java.util import Collections -from java.util.function import Consumer +from java.io import BufferedReader, InputStreamReader # type:ignore @UnresolvedImport +from java.lang import String # type:ignore @UnresolvedImport +from java.lang import Thread as JThread # type:ignore @UnresolvedImport +from java.util import Collections # type:ignore @UnresolvedImport +from java.util.function import Consumer # type:ignore @UnresolvedImport from jpype import JClass, JImplements, JOverride from pyghidra.internal.plugin.completions import PythonCodeCompleter diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/javac.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/javac.py index 034d47a93c..73c07b3b01 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/javac.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/javac.py @@ -29,8 +29,8 @@ COMPILER_OPTIONS = ["-target", "21", "-source", "21"] def _to_jar_(jar_path: Path, root: Path): - from java.io import ByteArrayOutputStream - from java.util.jar import JarEntry, JarOutputStream + from java.io import ByteArrayOutputStream # type:ignore @UnresolvedImport + from java.util.jar import JarEntry, JarOutputStream # type:ignore @UnresolvedImport out = ByteArrayOutputStream() with JarOutputStream(out) as jar: @@ -46,12 +46,12 @@ def _to_jar_(jar_path: Path, root: Path): class _CompilerDiagnosticListener: def __init__(self): - from javax.tools import Diagnostic + from javax.tools import Diagnostic # type:ignore @UnresolvedImport self.errors: List[Diagnostic] = [] @JOverride def report(self, diagnostic): - from javax.tools import Diagnostic + from javax.tools import Diagnostic # type:ignore @UnresolvedImport diagnostic: Diagnostic = diagnostic kind = diagnostic.getKind() @@ -71,10 +71,10 @@ def java_compile(src_path: Path, jar_path: Path): :raises ValueError: If an error occurs when compiling the Java source """ - from java.lang import System - from java.io import Writer - from java.nio.file import Path as JPath - from javax.tools import StandardLocation, ToolProvider + from java.lang import System # type:ignore @UnresolvedImport + from java.io import Writer # type:ignore @UnresolvedImport + from java.nio.file import Path as JPath # type:ignore @UnresolvedImport + from javax.tools import StandardLocation, ToolProvider # type:ignore @UnresolvedImport with tempfile.TemporaryDirectory() as out: outdir = Path(out).resolve() diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/launcher.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/launcher.py index 70ca192bd9..dcb80189df 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/launcher.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/launcher.py @@ -14,7 +14,6 @@ # limitations under the License. ## import contextlib -import ctypes import ctypes.util import html import importlib.metadata @@ -36,17 +35,17 @@ import jpype from jpype import imports, _jpype from packaging.version import Version -from .javac import java_compile -from .script import PyGhidraScript -from .version import ApplicationInfo, ExtensionDetails, MINIMUM_GHIDRA_VERSION +from pyghidra.javac import java_compile +from pyghidra.script import PyGhidraScript +from pyghidra.version import ApplicationInfo, ExtensionDetails, MINIMUM_GHIDRA_VERSION logger = logging.getLogger(__name__) @contextlib.contextmanager def _silence_java_output(stdout=True, stderr=True): - from java.io import OutputStream, PrintStream - from java.lang import System + from java.io import OutputStream, PrintStream # type:ignore @UnresolvedImport + from java.lang import System # type:ignore @UnresolvedImport out = System.out err = System.err null = PrintStream(OutputStream.nullOutputStream()) @@ -116,7 +115,7 @@ def _plugin_lock(): """ File lock for processing plugins """ - from java.io import RandomAccessFile + from java.io import RandomAccessFile # type:ignore @UnresolvedImport path = Path(tempfile.gettempdir()) / "pyghidra_plugin_lock" try: # Python doesn't have a file lock except for unix systems @@ -431,7 +430,7 @@ class PyGhidraLauncher: # Add extra class paths # Do this before installing plugins incase dependencies are needed if self.class_files: - from java.lang import ClassLoader + from java.lang import ClassLoader # type:ignore @UnresolvedImport gcl = ClassLoader.getSystemClassLoader() for path in self.class_files: gcl.addPath(path) @@ -451,7 +450,7 @@ class PyGhidraLauncher: self._layout = GhidraLauncher.initializeGhidraEnvironment() # import properties to register the property customizer - from . import properties as _ + from pyghidra import properties as _ # @UnusedImport _load_entry_points("pyghidra.pre_launch") @@ -654,7 +653,7 @@ class GuiPyGhidraLauncher(PyGhidraLauncher): @staticmethod def _get_thread(name: str): - from java.lang import Thread + from java.lang import Thread # type:ignore @UnresolvedImport for t in Thread.getAllStackTraces().keySet(): if t.getName() == name: return t @@ -662,11 +661,11 @@ class GuiPyGhidraLauncher(PyGhidraLauncher): def _launch(self): from ghidra import Ghidra - from java.lang import Runtime, Thread + from java.lang import Runtime, Thread # type:ignore @UnresolvedImport if sys.platform == "win32": appid = ctypes.c_wchar_p(self.app_info.name) - ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appid) + ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appid) # @UndefinedVariable stdout = _PyGhidraStdOut(sys.stdout) stderr = _PyGhidraStdOut(sys.stderr) diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/script.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/script.py index c99228d0d2..6609295689 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/script.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/script.py @@ -14,7 +14,6 @@ # limitations under the License. ## import functools -import importlib import importlib.util import inspect import logging @@ -157,7 +156,7 @@ class PyGhidraScript(dict): global _headless_interpreter from ghidra.util import SystemUtilities - from .ghidradoc import _Helper + from pyghidra.ghidradoc import _Helper if SystemUtilities.isInHeadlessMode() and _headless_interpreter is None: _headless_interpreter = jobj diff --git a/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/GhidraBuiltinsBuilder.java b/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/GhidraBuiltinsBuilder.java index 48e7cd344f..0a82e6e732 100644 --- a/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/GhidraBuiltinsBuilder.java +++ b/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/GhidraBuiltinsBuilder.java @@ -56,6 +56,16 @@ class GhidraBuiltinsBuilder { catch (IOException e) { e.printStackTrace(); } + + File pypredefDir = new File(doclet.getDestDir().getParentFile(), "pypredef"); + File pypredefFile = new File(pypredefDir, "ghidra.ghidra_builtins.pypredef"); + pypredefDir.mkdirs(); + try (PrintWriter printer = new PrintWriter(new FileWriter(pypredefFile))) { + process(printer); + } + catch (IOException e) { + e.printStackTrace(); + } } /** diff --git a/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubPackage.java b/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubPackage.java index c160cb5fba..18e9766666 100644 --- a/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubPackage.java +++ b/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubPackage.java @@ -15,20 +15,10 @@ */ package ghidra.doclets.typestubs; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; +import java.io.*; +import java.util.*; -import javax.lang.model.element.Element; -import javax.lang.model.element.PackageElement; -import javax.lang.model.element.TypeElement; +import javax.lang.model.element.*; /** * {@link PythonTypeStubElement} for a package

@@ -105,6 +95,16 @@ final class PythonTypeStubPackage extends PythonTypeStubElement catch (IOException e) { e.printStackTrace(); } + + File pypredefDir = new File(doclet.getDestDir().getParentFile(), "pypredef"); + File pypredefFile = new File(pypredefDir, packageName + ".pypredef"); + pypredefDir.mkdirs(); + try (PrintWriter printer = new PrintWriter(new FileWriter(pypredefFile))) { + process(printer, ""); + } + catch (IOException e) { + e.printStackTrace(); + } } /** diff --git a/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubType.java b/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubType.java index a6d34e97e4..056767d8ea 100644 --- a/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubType.java +++ b/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubType.java @@ -16,27 +16,12 @@ package ghidra.doclets.typestubs; import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.TypeParameterElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; +import javax.lang.model.element.*; +import javax.lang.model.type.*; import com.sun.source.doctree.DocTree; @@ -278,12 +263,11 @@ class PythonTypeStubType extends PythonTypeStubElement { for (PythonTypeStubNestedType nested : getNestedTypes()) { nested.process(printer, indent); } + printClassLiteralField(printer, indent); for (VariableElement field : getFields()) { printField(field, printer, indent, isStatic(field)); } - if (!getFields().isEmpty()) { - printer.println(); - } + printer.println(); ListIterator methodIterator = getMethods().listIterator(); while (methodIterator.hasNext()) { PythonTypeStubMethod method = methodIterator.next(); @@ -342,6 +326,17 @@ class PythonTypeStubType extends PythonTypeStubElement { } } + /** + * Prints the class literal field to the provided printer + * + * @param printer the printer + * @param indent the indentation + */ + void printClassLiteralField(PrintWriter printer, String indent) { + printer.print(indent); + printer.println("class_: " + applyClassVar(Class.class.getName())); + } + /** * Wraps the provided type in typing.ClassVar *