diff --git a/cldk/analysis/c/clang/clang_analyzer.py b/cldk/analysis/c/clang/clang_analyzer.py index e828632..13672fa 100644 --- a/cldk/analysis/c/clang/clang_analyzer.py +++ b/cldk/analysis/c/clang/clang_analyzer.py @@ -3,9 +3,8 @@ from clang.cindex import Config from pathlib import Path from typing import List, Optional -from cldk.models.c import CFunction, CMacro, CCallSite, CTranslationUnit, CApplication +from cldk.models.c import CFunction, CCallSite, CTranslationUnit, CApplication import logging -from ipdb import set_trace from cldk.models.c.models import CInclude, CParameter, CVariable, StorageClass @@ -34,14 +33,15 @@ def find_libclang() -> str: # On Linux, we check various common installation paths elif system == "Linux": + from pathlib import Path + + lib_paths = [Path("/usr/lib"), Path("/usr/lib64")] possible_paths = [ - "/usr/lib/llvm-14/lib/libclang.so", - "/usr/lib/llvm-13/lib/libclang.so", - "/usr/lib/llvm-12/lib/libclang.so", - "/usr/lib/x86_64-linux-gnu/libclang-14.so.1", - "/usr/lib/libclang.so", + str(p) for base in lib_paths if base.exists() + for p in base.rglob("libclang*.so*") ] - install_instructions = "Install libclang using: sudo apt-get install libclang-dev" + + install_instructions = "Install libclang development package using your system's package manager" else: raise RuntimeError(f"Unsupported operating system: {system}") diff --git a/cldk/analysis/java/codeanalyzer/codeanalyzer.py b/cldk/analysis/java/codeanalyzer/codeanalyzer.py index 97b0494..6ddd1e3 100644 --- a/cldk/analysis/java/codeanalyzer/codeanalyzer.py +++ b/cldk/analysis/java/codeanalyzer/codeanalyzer.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. ################################################################################ +from itertools import chain, groupby from pdb import set_trace import re import json @@ -863,20 +864,20 @@ def get_class_call_graph(self, qualified_class_name: str, method_name: str | Non return graph_edges def get_all_entry_point_methods(self) -> Dict[str, Dict[str, JCallable]]: - """Returns a dictionary of all entry point methods in the Java code with - qualified class name as the key and a dictionary of methods in that class as the value. + """Returns a dictionary of all entry point methods in the Java code. Returns: - Dict[str, Dict[str, JCallable]]: A dictionary of dictionaries of entry point - methods in the Java code. + Dict[str, Dict[str, JCallable]]: A dictionary of all entry point methods in the Java code. """ - - class_method_dict = {} - class_dict = self.get_all_classes() - for k, v in class_dict.items(): - entry_point_methods = {method_name: callable_decl for (method_name, callable_decl) in v.callable_declarations.items() if callable_decl.is_entry_point is True} - class_method_dict[k] = entry_point_methods - return class_method_dict + methods = chain.from_iterable( + ((typename, method, callable) + for method, callable in methods.items() if callable.is_entrypoint) + for typename, methods in self.get_all_methods_in_application().items() + ) + return { + typename: {method: callable for _, method, callable in group} + for typename, group in groupby(methods, key=lambda x: x[0]) + } def get_all_entry_point_classes(self) -> Dict[str, JType]: """Returns a dictionary of all entry point classes in the Java code. @@ -886,8 +887,8 @@ def get_all_entry_point_classes(self) -> Dict[str, JType]: with qualified class names as keys. """ - class_dict = {} - symtab = self.get_symbol_table() - for val in symtab.values(): - class_dict.update((k, v) for k, v in val.type_declarations.items() if v.is_entry_point is True) - return class_dict + return { + typename: klass + for typename, klass in self.get_all_classes().items() + if klass.is_entrypoint_class + } diff --git a/cldk/models/java/__init__.py b/cldk/models/java/__init__.py index 1a68d0c..8bdfc25 100644 --- a/cldk/models/java/__init__.py +++ b/cldk/models/java/__init__.py @@ -26,6 +26,4 @@ JGraphEdges, ) -from .constants_namespace import ConstantsNamespace - __all__ = ["JApplication", "JCallable", "JType", "JCompilationUnit", "JGraphEdges", "ConstantsNamespace"] diff --git a/cldk/models/java/constants_namespace.py b/cldk/models/java/constants_namespace.py deleted file mode 100644 index 17c616d..0000000 --- a/cldk/models/java/constants_namespace.py +++ /dev/null @@ -1,41 +0,0 @@ -################################################################################ -# Copyright IBM Corporation 2024 -# -# 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. -################################################################################ - -""" -Constants Namespace module -""" - - -class ConstantsNamespace: - @property - def ENTRY_POINT_SERVLET_CLASSES(self): - return ("javax.servlet.GenericServlet", "javax.servlet.Filter", "javax.servlet.http.HttpServlet") - - @property - def ENTRY_POINT_METHOD_SERVLET_PARAM_TYPES(self): - return ("javax.servlet.ServletRequest", "javax.servlet.ServletResponse", "javax.servlet.http.HttpServletRequest", "javax.servlet.http.HttpServletResponse") - - @property - def ENTRY_POINT_METHOD_JAVAX_WS_ANNOTATIONS(self): - return ("javax.ws.rs.POST", "javax.ws.rs.PUT", "javax.ws.rs.GET", "javax.ws.rs.HEAD", "javax.ws.rs.DELETE") - - @property - def ENTRY_POINT_CLASS_SPRING_ANNOTATIONS(self): - return ("Controller", "RestController") - - @property - def ENTRY_POINT_METHOD_SPRING_ANNOTATIONS(self): - return ("GetMapping", "PathMapping", "PostMapping", "PutMapping", "RequestMapping", "DeleteMapping") diff --git a/cldk/models/java/models.py b/cldk/models/java/models.py index 2579bb8..2a98964 100644 --- a/cldk/models/java/models.py +++ b/cldk/models/java/models.py @@ -23,10 +23,6 @@ from pdb import set_trace from pydantic import BaseModel, field_validator, model_validator -from .constants_namespace import ConstantsNamespace - -constants = ConstantsNamespace() -context_concrete_class = ContextVar("context_concrete_class") # context var to store class concreteness _CALLABLES_LOOKUP_TABLE = dict() @@ -179,6 +175,7 @@ class JCallable(BaseModel): referenced_types: List[str] accessed_fields: List[str] call_sites: List[JCallSite] + is_entrypoint: bool = False variable_declarations: List[JVariableDeclaration] cyclomatic_complexity: int | None @@ -188,33 +185,6 @@ def __hash__(self): """ return hash(self.declaration) - @model_validator(mode="after") - def detect_entrypoint_method(self): - # check first if the class in which this method exists is concrete or not, by looking at the context var - if context_concrete_class.get(): - # convert annotations to the form GET, POST even if they are @GET or @GET('/ID') etc. - annotations_cleaned = [match for annotation in self.annotations for match in re.findall(r"@(.*?)(?:\(|$)", annotation)] - - param_type_list = [val.type for val in self.parameters] - # check the param types against known servlet param types - if any(substring in string for substring in param_type_list for string in constants.ENTRY_POINT_METHOD_SERVLET_PARAM_TYPES): - # check if this method is over-riding (only methods that override doGet / doPost etc. will be flagged as first level entry points) - if "Override" in annotations_cleaned: - self.is_entry_point = True - return self - - # now check the cleaned annotations against known javax ws annotations - if any(substring in string for substring in annotations_cleaned for string in constants.ENTRY_POINT_METHOD_JAVAX_WS_ANNOTATIONS): - self.is_entry_point = True - return self - - # check the cleaned annotations against known spring rest method annotations - if any(substring in string for substring in annotations_cleaned for string in constants.ENTRY_POINT_METHOD_SPRING_ANNOTATIONS): - self.is_entry_point = True - return self - return self - - class JType(BaseModel): """Represents a Java class or interface. @@ -257,44 +227,12 @@ class JType(BaseModel): modifiers: List[str] = [] annotations: List[str] = [] parent_type: str + is_entrypoint_class: bool = False nested_type_declerations: List[str] = [] callable_declarations: Dict[str, JCallable] = {} field_declarations: List[JField] = [] enum_constants: List[JEnumConstant] = [] - # first get the data in raw form and check if the class is concrete or not, before any model validation is done - # for this we assume if a class is not an interface or abstract it is concrete - # for abstract classes we will check the modifiers - @model_validator(mode="before") - def check_concrete_class(cls, values): - """Detects if the class is concrete based on its properties.""" - values["is_concrete_class"] = False - if values.get("is_class_or_interface_declaration") and not values.get("is_interface"): - if "abstract" not in values.get("modifiers"): - values["is_concrete_class"] = True - # since the methods in this class need access to the concrete class flag, - # we will store this in a context var - this is a hack - token = context_concrete_class.set(values["is_concrete_class"]) - return values - - # after model validation is done we populate the is_entry_point flag by checking - # if the class extends or implements known servlet classes - @model_validator(mode="after") - def check_concrete_entry_point(self): - """Detects if the class is entry point based on its properties.""" - if self.is_concrete_class: - if any(substring in string for substring in (self.extends_list + self.implements_list) for string in constants.ENTRY_POINT_SERVLET_CLASSES): - self.is_entry_point = True - return self - # Handle spring classes - # clean annotations - take out @ and any paranehesis along with info in them. - annotations_cleaned = [match for annotation in self.annotations for match in re.findall(r"@(.*?)(?:\(|$)", annotation)] - if any(substring in string for substring in annotations_cleaned for string in constants.ENTRY_POINT_CLASS_SPRING_ANNOTATIONS): - self.is_entry_point = True - return self - # context_concrete.reset() - return self - class JCompilationUnit(BaseModel): """Represents a compilation unit in Java. diff --git a/poetry.lock b/poetry.lock index e1cd327..9f6432d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -213,33 +213,33 @@ lxml = ["lxml"] [[package]] name = "black" -version = "24.10.0" +version = "25.1.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" files = [ - {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, - {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, - {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, - {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, - {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, - {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, - {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, - {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, - {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, - {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, - {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, - {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, - {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, - {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, - {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, - {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, - {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, - {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, - {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, - {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, - {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, - {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, + {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, + {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, + {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, + {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, + {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, + {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, + {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, + {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, + {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, + {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, + {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, + {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, + {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, + {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, + {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, + {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, + {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, + {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, + {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, + {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, + {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, + {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, ] [package.dependencies] @@ -922,6 +922,21 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "ipdb" +version = "0.13.13" +description = "IPython-enabled pdb" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4"}, + {file = "ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726"}, +] + +[package.dependencies] +decorator = {version = "*", markers = "python_version >= \"3.11\""} +ipython = {version = ">=7.31.1", markers = "python_version >= \"3.11\""} + [[package]] name = "ipykernel" version = "6.29.5" @@ -1720,23 +1735,22 @@ files = [ [[package]] name = "mkdocstrings" -version = "0.27.0" +version = "0.28.0" description = "Automatic documentation from sources, for MkDocs." optional = false python-versions = ">=3.9" files = [ - {file = "mkdocstrings-0.27.0-py3-none-any.whl", hash = "sha256:6ceaa7ea830770959b55a16203ac63da24badd71325b96af950e59fd37366332"}, - {file = "mkdocstrings-0.27.0.tar.gz", hash = "sha256:16adca6d6b0a1f9e0c07ff0b02ced8e16f228a9d65a37c063ec4c14d7b76a657"}, + {file = "mkdocstrings-0.28.0-py3-none-any.whl", hash = "sha256:84cf3dc910614781fe0fee46ce8006fde7df6cc7cca2e3f799895fb8a9170b39"}, + {file = "mkdocstrings-0.28.0.tar.gz", hash = "sha256:df20afef1eafe36ba466ae20732509ecb74237653a585f5061937e54b553b4e0"}, ] [package.dependencies] -click = ">=7.0" Jinja2 = ">=2.11.1" Markdown = ">=3.6" MarkupSafe = ">=1.1" mkdocs = ">=1.4" -mkdocs-autorefs = ">=1.2" -platformdirs = ">=2.2" +mkdocs-autorefs = ">=1.3" +mkdocs-get-deps = ">=0.2" pymdown-extensions = ">=6.3" [package.extras] @@ -1746,19 +1760,19 @@ python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] [[package]] name = "mkdocstrings-python" -version = "1.13.0" +version = "1.14.5" description = "A Python handler for mkdocstrings." optional = false python-versions = ">=3.9" files = [ - {file = "mkdocstrings_python-1.13.0-py3-none-any.whl", hash = "sha256:b88bbb207bab4086434743849f8e796788b373bd32e7bfefbf8560ac45d88f97"}, - {file = "mkdocstrings_python-1.13.0.tar.gz", hash = "sha256:2dbd5757e8375b9720e81db16f52f1856bf59905428fd7ef88005d1370e2f64c"}, + {file = "mkdocstrings_python-1.14.5-py3-none-any.whl", hash = "sha256:ac394f273ae298aeaa6be4506768f05e61bd7c8119437ea98553354b1185c469"}, + {file = "mkdocstrings_python-1.14.5.tar.gz", hash = "sha256:8582eeac8cce952f395d76ec636fc814757cba7d8458aa75ba0529a3aa10d98c"}, ] [package.dependencies] griffe = ">=0.49" mkdocs-autorefs = ">=1.2" -mkdocstrings = ">=0.26" +mkdocstrings = ">=0.28" [[package]] name = "mypy-extensions" @@ -2648,13 +2662,13 @@ extra = ["pygments (>=2.19.1)"] [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -2668,17 +2682,17 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments [[package]] name = "pytest-cov" -version = "5.0.0" +version = "6.0.0" description = "Pytest plugin for measuring coverage." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, - {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, + {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, + {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, ] [package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} +coverage = {version = ">=7.5", extras = ["toml"]} pytest = ">=4.6" [package.extras] @@ -3789,4 +3803,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.11" -content-hash = "d131ab08a6fc30e2eaf7ddd4251725cdec8095affe329342e38aa137038cf10b" +content-hash = "930ff9d1dabe3d1bb9842bcc4629dceb634756a6e01c4c08bb107ed7811907ca" diff --git a/pyproject.toml b/pyproject.toml index 538e261..5ef5d96 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,10 +42,15 @@ tree-sitter-c = "0.23.4" tree-sitter-go = "0.23.4" tree-sitter-python = "0.23.6" tree-sitter-javascript = "0.23.1" -# Test dependencies -mkdocs-material = {extras = ["imaging"], version = "^9.6.2"} +libclang = "^18.1.1" +clang = "^17.0.6" [tool.poetry.group.dev.dependencies] +ipdb = "^0.13.13" + + +[tool.poetry.group.test.dependencies] +# Test dependencies toml = "^0.10.2" pytest = "8.3.4" pytest-pspec = "^0.0.4" @@ -59,7 +64,7 @@ jupyter = "^1.1.1" [tool.poetry.group.doc.dependencies] -mkdocs-material = "^9.6.2" +mkdocs-material = {extras = ["imaging"], version = "^9.6.2"} mkdocs-autorefs = "^1.3.0" mkdocs-get-deps = "^0.2.0" mkdocs-material-extensions = "^1.3.1" diff --git a/tests/analysis/java/test_jcodeanalyzer.py b/tests/analysis/java/test_jcodeanalyzer.py index 1ed6d99..da50062 100644 --- a/tests/analysis/java/test_jcodeanalyzer.py +++ b/tests/analysis/java/test_jcodeanalyzer.py @@ -444,6 +444,55 @@ def test_get_all_methods_in_application(test_fixture, analysis_json): assert isinstance(callable, JCallable) +def test_get_all_entrypoint_methods_in_application(test_fixture, codeanalyzer_jar_path): + """It should return all of the entrypoint methods in an application""" + code_analyzer = JCodeanalyzer( + project_dir=test_fixture, + source_code=None, + analysis_backend_path=codeanalyzer_jar_path, + analysis_json_path=None, + analysis_level=AnalysisLevel.symbol_table, + use_graalvm_binary=False, + eager_analysis=False, + target_files=None, + ) + entrypoint_methods = code_analyzer.get_all_entry_point_methods() + assert entrypoint_methods is not None + assert isinstance(entrypoint_methods, Dict) + assert len(entrypoint_methods) > 0 + # Validate structure + for _, method in entrypoint_methods.items(): + assert method is not None + assert isinstance(method, Dict) + for _, callable in method.items(): + assert callable is not None + assert isinstance(callable, JCallable) + assert callable.is_entrypoint + + +def test_get_all_entrypoint_classes_in_the_application(test_fixture, codeanalyzer_jar_path): + """It should return all of the entrypoint classes in an application""" + code_analyzer = JCodeanalyzer( + project_dir=test_fixture, + source_code=None, + analysis_backend_path=codeanalyzer_jar_path, + analysis_json_path=None, + analysis_level=AnalysisLevel.symbol_table, + use_graalvm_binary=False, + eager_analysis=False, + target_files=None, + ) + entrypoint_classes = code_analyzer.get_all_entry_point_classes() + assert entrypoint_classes is not None + assert isinstance(entrypoint_classes, Dict) + assert len(entrypoint_classes) > 0 + # Validate structure + for _, cls in entrypoint_classes.items(): + assert cls is not None + assert isinstance(cls, JType) + assert cls.is_entrypoint_class + + def test_get_all_classes(test_fixture, analysis_json): """It should return all of the classes in an application""" diff --git a/tests/conftest.py b/tests/conftest.py index 30ba3e1..3dd00ab 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -70,7 +70,7 @@ def codeanalyzer_jar_path(): # Load the configuration config = toml.load(pyproject_path) - return Path(config["tool"]["cldk"]["testing"]["codeanalyzer-jar-path"]) / "2.0.0" + return Path(config["tool"]["cldk"]["testing"]["codeanalyzer-jar-path"]) / "2.1.0" @pytest.fixture(scope="session", autouse=True) diff --git a/tests/resources/java/codeanalyzer_jars/2.1.0/codeanalyzer-2.1.0.jar b/tests/resources/java/codeanalyzer_jars/2.1.0/codeanalyzer-2.1.0.jar new file mode 100644 index 0000000..b881df5 Binary files /dev/null and b/tests/resources/java/codeanalyzer_jars/2.1.0/codeanalyzer-2.1.0.jar differ