diff --git a/Ghidra/Features/PyGhidra/src/main/py/pyproject.toml b/Ghidra/Features/PyGhidra/src/main/py/pyproject.toml index ec08544cef..1daf31b1e8 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/pyproject.toml +++ b/Ghidra/Features/PyGhidra/src/main/py/pyproject.toml @@ -30,6 +30,7 @@ classifiers = [ ] dependencies = [ "Jpype1>=1.5.0", + "packaging" ] [project.optional-dependencies] 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 9030d4c7c0..c31919d3a9 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/launcher.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/launcher.py @@ -28,12 +28,13 @@ import subprocess import sys import tempfile import threading +from importlib.machinery import ModuleSpec from pathlib import Path from typing import List, NoReturn, Tuple, Union import jpype from jpype import imports, _jpype -from importlib.machinery import ModuleSpec +from packaging.version import Version from .javac import java_compile from .script import PyGhidraScript @@ -199,7 +200,7 @@ class PyGhidraLauncher: msg = "Cannot launch from repo because Ghidra has not been compiled " \ "with Eclipse or Gradle." self._report_fatal_error("Ghidra not built", msg, ValueError(msg)) - + self.class_path = [str(classpath)] if not self._java_home: self._launch_support = launch_support @@ -225,10 +226,10 @@ class PyGhidraLauncher: properties = [] root = self._install_dir - + if self._dev_mode: root = root / "Ghidra" / "RuntimeScripts" / "Common" - + launch_properties = root / "support" / "launch.properties" for line in Path(launch_properties).read_text().splitlines(): @@ -349,7 +350,7 @@ class PyGhidraLauncher: Checks if the currently installed Ghidra version is supported. The launcher will report the problem and terminate if it is not supported. """ - if self.app_info.version < MINIMUM_GHIDRA_VERSION: + if Version(self.app_info.version) < Version(MINIMUM_GHIDRA_VERSION): msg = f"Ghidra version {self.app_info.version} is not supported" + os.linesep + \ f"The minimum required version is {MINIMUM_GHIDRA_VERSION}" self._report_fatal_error("Unsupported Version", msg, ValueError(msg)) @@ -677,15 +678,15 @@ def _run_mac_app(): # this runs the event loop # it is required for the GUI to show up from ctypes import c_void_p, c_double, c_uint64, c_int64, c_int32, c_bool, CFUNCTYPE - + CoreFoundation = ctypes.cdll.LoadLibrary(ctypes.util.find_library("CoreFoundation")) - + def get_function(name, restype, *argtypes): res = getattr(CoreFoundation, name) res.argtypes = [arg for arg in argtypes] res.restype = restype return res - + CFRunLoopTimerCallback = CFUNCTYPE(None, c_void_p, c_void_p) kCFRunLoopDefaultMode = c_void_p.in_dll(CoreFoundation, "kCFRunLoopDefaultMode") kCFRunLoopRunFinished = c_int32(1) @@ -693,10 +694,10 @@ def _run_mac_app(): INF_TIME = c_double(1.0e20) FIRE_ONCE = c_double(0) kCFAllocatorDefault = NULL - + CFRunLoopGetCurrent = get_function("CFRunLoopGetCurrent", c_void_p) CFRelease = get_function("CFRelease", None, c_void_p) - + CFRunLoopTimerCreate = get_function( "CFRunLoopTimerCreate", c_void_p, @@ -708,19 +709,19 @@ def _run_mac_app(): CFRunLoopTimerCallback, c_void_p ) - + CFRunLoopAddTimer = get_function("CFRunLoopAddTimer", None, c_void_p, c_void_p, c_void_p) CFRunLoopRunInMode = get_function("CFRunLoopRunInMode", c_int32, c_void_p, c_double, c_bool) - + @CFRunLoopTimerCallback def dummy_timer(timer, info): # this doesn't need to do anything # CFRunLoopTimerCreate just needs a valid callback return - + timer = CFRunLoopTimerCreate(kCFAllocatorDefault, INF_TIME, FIRE_ONCE, 0, 0, dummy_timer, NULL) CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode) CFRelease(timer) - + while CFRunLoopRunInMode(kCFRunLoopDefaultMode, INF_TIME, False) != kCFRunLoopRunFinished: pass 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 e8c4ec9ba8..fe8555e482 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 @@ -188,6 +188,7 @@ class GhidraBuiltinsBuilder { * @param printer the printer */ private void printScriptImports(PrintWriter printer) { + printer.println("from __future__ import annotations"); printer.println("import collections.abc"); printer.println("import typing"); printer.println("from warnings import deprecated # type: ignore"); diff --git a/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/JavadocConverter.java b/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/JavadocConverter.java index 2b12f0a466..72d7623015 100644 --- a/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/JavadocConverter.java +++ b/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/JavadocConverter.java @@ -553,7 +553,7 @@ public class JavadocConverter extends DocConverter { private static String sanitizeQualifiedName(ExecutableElement el, TypeMirror type) { Element self = el.getEnclosingElement(); PackageElement pkg = PythonTypeStubElement.getPackage(self); - return PythonTypeStubElement.sanitizeQualifiedName(self, type, pkg); + return PythonTypeStubElement.sanitizeQualifiedName(type, pkg); } /** diff --git a/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubElement.java b/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubElement.java index e9a9205127..1c60919318 100644 --- a/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubElement.java +++ b/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubElement.java @@ -23,7 +23,6 @@ import java.util.List; import java.util.Set; import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; import javax.lang.model.element.Name; import javax.lang.model.element.PackageElement; @@ -106,24 +105,6 @@ abstract class PythonTypeStubElement { return false; } - /** - * Gets the type string for the provided type and quotes if necessary

- * - * This string value is safe to be used as a parameter or return type - * as well as for use in a generic type. - * - * @param self the type to become typing.Self if encountered - * @param type the type to get the string for - * @return the type string - */ - String getTypeString(Element self, TypeMirror type) { - String typeName = sanitizeQualifiedName(self, type); - if (isSamePackage(type) && !typeName.equals("typing.Self")) { - typeName = '"' + typeName + '"'; - } - return typeName; - } - /** * Gets the Python safe name for this element * @@ -248,18 +229,17 @@ abstract class PythonTypeStubElement { /** * Makes the provided type Python safe if necessary * - * @param self the type to become typing.Self if encountered * @param type the type to make Python safe * @param pkg the current package * @return the Python safe type name */ - static String sanitize(Element self, TypeMirror type, PackageElement pkg) { + static String sanitize(TypeMirror type, PackageElement pkg) { return switch (type.getKind()) { case DECLARED -> throw new RuntimeException( "declared types should use the qualified name"); case ARRAY -> { TypeMirror component = ((ArrayType) type).getComponentType(); - yield "jpype.JArray[" + sanitizeQualifiedName(self, component, pkg) + "]"; + yield "jpype.JArray[" + sanitizeQualifiedName(component, pkg) + "]"; } case BOOLEAN -> "jpype.JBoolean"; case BYTE -> "jpype.JByte"; @@ -270,55 +250,31 @@ abstract class PythonTypeStubElement { case LONG -> "jpype.JLong"; case SHORT -> "jpype.JShort"; case TYPEVAR -> type.toString(); - case WILDCARD -> getWildcardVarName(self, (WildcardType) type, pkg); + case WILDCARD -> getWildcardVarName((WildcardType) type, pkg); default -> throw new RuntimeException("unexpected TypeKind " + type.getKind()); }; } - /** - * Checks if the provided type is the same as the provided element - * - * @param self the element of the type to become typing.Self - * @param type the type to check - * @return true if the inputs represent the same type - */ - static final boolean isSelfType(Element self, TypeMirror type) { - if (self.getKind() == ElementKind.ENUM) { - // typing.Self is usually invalid here - return false; - } - if (type instanceof DeclaredType dt) { - return self.equals(dt.asElement()); - } - return false; - } - /** * Makes the qualified name for the provided type Python safe if necessary * - * @param self the type to become typing.Self if encountered * @param type the type to make Python safe * @return the Python safe qualified type name */ - final String sanitizeQualifiedName(Element self, TypeMirror type) { - return sanitizeQualifiedName(self, type, pkg); + final String sanitizeQualifiedName(TypeMirror type) { + return sanitizeQualifiedName(type, pkg); } /** * Makes the qualified name for the provided type Python safe if necessary

* * The provided package is used to check each type and generic components. - * If they require a "forward declaration", it is handled accordingly. * - * @param self the type to become typing.Self if encountered * @param type the type to make Python safe * @param pkg the current package * @return the Python safe qualified type name */ - static final String sanitizeQualifiedName(Element self, TypeMirror type, PackageElement pkg) { - if (isSelfType(self, type)) { - return "typing.Self"; - } + static final String sanitizeQualifiedName(TypeMirror type, PackageElement pkg) { if (type instanceof DeclaredType dt) { TypeElement el = (TypeElement) dt.asElement(); PackageElement typePkg = getPackage(el); @@ -341,11 +297,41 @@ abstract class PythonTypeStubElement { return name; } Iterable it = () -> args.stream() - .map(paramType -> sanitizeQualifiedName(self, paramType, pkg)) + .map(paramType -> sanitizeQualifiedName(paramType, pkg)) .iterator(); return name + "[" + String.join(", ", it) + "]"; } - return sanitize(self, type, pkg); + return sanitize(type, pkg); + } + + /** + * Recursively adds the type and it's generic parameters to the provided imports set. + * + * @param imports the set of imported types + * @param type the type to add to the imports + */ + static void addNeededTypes(Set imports, TypeMirror type) { + switch (type.getKind()) { + case DECLARED: + DeclaredType dt = (DeclaredType) type;; + imports.add((TypeElement) dt.asElement()); + for (TypeMirror genericType : dt.getTypeArguments()) { + addNeededTypes(imports, genericType); + } + break; + case WILDCARD: + WildcardType wt = (WildcardType) type; + TypeMirror base = wt.getExtendsBound(); + if (base == null) { + base = wt.getSuperBound(); + } + if (base != null) { + addNeededTypes(imports, base); + } + break; + default: + break; + } } /** @@ -411,18 +397,17 @@ abstract class PythonTypeStubElement { /** * Gets the name for a wildcard type if possible * - * @param self the type to become typing.Self if encountered * @param type the wildcard type * @param pkg the current package * @return the determined type name if possible otherwise typing.Any */ - private static String getWildcardVarName(Element self, WildcardType type, PackageElement pkg) { + private static String getWildcardVarName(WildcardType type, PackageElement pkg) { TypeMirror base = type.getExtendsBound(); if (base == null) { base = type.getSuperBound(); } if (base != null) { - return sanitizeQualifiedName(self, base, pkg); + return sanitizeQualifiedName(base, pkg); } return "typing.Any"; } diff --git a/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubMethod.java b/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubMethod.java index 34523e9b5c..62b6a08828 100644 --- a/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubMethod.java +++ b/GhidraBuild/BuildFiles/Doclets/src/main/java/ghidra/doclets/typestubs/PythonTypeStubMethod.java @@ -87,7 +87,6 @@ final class PythonTypeStubMethod extends PythonTypeStubElement typevars; Set imports; @@ -111,7 +110,6 @@ final class PythonTypeStubMethod extends PythonTypeStubElement parameters = el.getParameters(); - TypeMirror resType = el.getReturnType(); + List parameters = getParameterTypes(); // make the set big enough for all paramters and the return type imports = new HashSet<>(parameters.size() + 1); - if (resType instanceof DeclaredType dt) { - imports.add((TypeElement) dt.asElement()); - } - - for (VariableElement param : parameters) { - if (param.asType() instanceof DeclaredType dt) { - imports.add((TypeElement) dt.asElement()); - } + addNeededTypes(imports, getReturnType()); + for (TypeMirror param : parameters) { + addNeededTypes(imports, param); } return imports; @@ -329,7 +321,7 @@ final class PythonTypeStubMethod extends PythonTypeStubElement */ private void process(PrintWriter printer, String indent) { writeJavaDoc(printer, indent, ""); + printer.println("from __future__ import annotations"); printer.println("import collections.abc"); printer.println("import datetime"); printer.println("import typing"); 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 c1b18829be..a6d34e97e4 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 @@ -125,18 +125,13 @@ class PythonTypeStubType extends PythonTypeStubElement { imports.add((TypeElement) dt.asElement()); } for (TypeMirror iface : el.getInterfaces()) { - if (iface instanceof DeclaredType dt) { - imports.add((TypeElement) dt.asElement()); - } + addNeededTypes(imports, iface); } for (PythonTypeStubNestedType nested : getNestedTypes()) { imports.addAll(nested.getImportedTypes()); } for (VariableElement field : getFields()) { - TypeMirror fieldType = field.asType(); - if (fieldType instanceof DeclaredType dt) { - imports.add((TypeElement) dt.asElement()); - } + addNeededTypes(imports, field.asType()); } for (PythonTypeStubMethod method : getMethods()) { imports.addAll(method.getImportedTypes()); @@ -327,7 +322,7 @@ class PythonTypeStubType extends PythonTypeStubElement { else { TypeMirror type = field.asType(); printer.print(": "); - String sanitizedType = getTypeString(el, type); + String sanitizedType = sanitizeQualifiedName(type); // only one of these may be applied // prefer Final over ClassVar @@ -648,11 +643,7 @@ class PythonTypeStubType extends PythonTypeStubElement { } return OBJECT_NAME; } - return sanitizeQualifiedName(el, base); - } - - private String sanitizeQualifiedName(TypeMirror type) { - return sanitizeQualifiedName(el, type); + return sanitizeQualifiedName(base); } /**