Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions droidasc/asc_client/asc_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
_DexManager = None
_FindRefManager = None
_decompile_dex_bytes = None
_decompile_dex_bytes_with_metadata = None
_DEX = None


Expand Down Expand Up @@ -36,19 +37,20 @@ def _install_pure_python_mutf8_shim():


def _lazy_import():
global _DexManager, _FindRefManager, _decompile_dex_bytes, _DEX
global _DexManager, _FindRefManager, _decompile_dex_bytes, _decompile_dex_bytes_with_metadata, _DEX
if _DexManager is not None:
return

_install_pure_python_mutf8_shim()
from droidasc.asc_core.core.dex.dex_manager import DexManager
from droidasc.asc_core.findrefs.findrefs_manager import FindRefManager
from droidasc.asc_core.utils.tinydex import DEX
from droidasc.asc_core.utils.decompiler import decompile_dex_bytes
from droidasc.asc_core.utils.decompiler import decompile_dex_bytes, decompile_dex_bytes_with_metadata

_DexManager = DexManager
_FindRefManager = FindRefManager
_decompile_dex_bytes = decompile_dex_bytes
_decompile_dex_bytes_with_metadata = decompile_dex_bytes_with_metadata
_DEX = DEX


Expand All @@ -62,6 +64,12 @@ def getclass(self, dex_buf : bytes, dalvik_class : str) -> str:
new_dex_bytes = manager.extract_and_rebuild(dalvik_class)
return _decompile_dex_bytes(new_dex_bytes, dalvik_class)

def getclass_with_metadata(self, dex_buf : bytes, dalvik_class : str):
_lazy_import()
manager = _DexManager(memoryview(dex_buf), debug=self.debug)
new_dex_bytes = manager.extract_and_rebuild(dalvik_class)
return _decompile_dex_bytes_with_metadata(new_dex_bytes, dalvik_class)

def _format_method(self, dex, midx : int) -> str:
method = dex.methods[midx]
return f"{method.cls.fullname}->{method.name}"
Expand Down
181 changes: 173 additions & 8 deletions droidasc/asc_client/gui/app.py

Large diffs are not rendered by default.

21 changes: 15 additions & 6 deletions droidasc/asc_client/gui/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ def _normalize_class_query(name : str, fuzzy : bool):
return format_class_name(name)


def build_find_query(find_type : str, value : str, class_name = None, fuzzy_class : bool = False):
def build_find_query(
find_type : str, value : str, class_name = None, fuzzy_class : bool = False,
exact_member : bool = False,
):
find_type = _REF_SEARCH_TYPES.get(find_type, find_type)
if find_type == "string":
return "string", {"string": value}
Expand All @@ -92,9 +95,10 @@ def build_find_query(find_type : str, value : str, class_name = None, fuzzy_clas
if class_name is None and not value:
raise ValueError(f"{find_type} query needs at least one of class or {find_type} name")

member = [value, True] if exact_member and value else value or None
if class_name is None:
return find_type, {find_type: {"class": None, find_type: value or None}}
return find_type, {find_type: {"class": [class_name, not fuzzy_class], find_type: value or None}}
return find_type, {find_type: {"class": None, find_type: member}}
return find_type, {find_type: {"class": [class_name, not fuzzy_class], find_type: member}}


def parse_result_line(line : str):
Expand Down Expand Up @@ -238,6 +242,10 @@ def iter_filtered_classes(self, keyword : str, limit : int):
return ret

def get_source(self, dalvik_class : str):
dex_name, source, _references = self.get_source_with_metadata(dalvik_class)
return dex_name, source

def get_source_with_metadata(self, dalvik_class : str):
with self._source_lock:
old = self.source_cache.get(dalvik_class)
if old is not None:
Expand All @@ -259,10 +267,10 @@ def get_source(self, dalvik_class : str):
with _GUI_MP_LOCK:
snapshot = _snapshot_sys_modules()
try:
source = AscHandler(self.debug).getclass(dex_buf, dalvik_class)
source, references = AscHandler(self.debug).getclass_with_metadata(dex_buf, dalvik_class)
finally:
_restore_sys_modules(snapshot)
ret = (dex_name, source)
ret = (dex_name, source, references)
with self._source_lock:
self.source_cache[dalvik_class] = ret
_debug_log(
Expand Down Expand Up @@ -505,11 +513,12 @@ def search(
value : str,
class_name = None,
fuzzy_class : bool = False,
exact_member : bool = False,
max_workers = None,
progress_callback = None,
result_callback = None,
):
find_type, find = build_find_query(find_type, value, class_name, fuzzy_class)
find_type, find = build_find_query(find_type, value, class_name, fuzzy_class, exact_member)
workers = self.get_effective_search_workers(max_workers)
_debug_log(
self.debug,
Expand Down
43 changes: 43 additions & 0 deletions droidasc/asc_client/gui/source_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,49 @@ def rename_identifier_in_range(text : str, start : int, end : int, old : str, ne
return text[:start] + "".join(out) + text[end:], len(spans)


def remap_ranges_after_replacements(ranges, spans, replacement_length : int):
def remap(point):
shift = 0
for start, end in spans:
if point < end:
break
shift += replacement_length - (end - start)
return point + shift

return [
(remap(item[0]), remap(item[1]), *item[2:])
for item in ranges
]


def member_reference_at_offset(references, offset : int):
return next(
(item for item in references if item[0] <= offset < item[1]),
None,
)


def find_member_declaration(references, member_type : str, member_name : str, descriptor : str):
declarations = [
item for item in references
if item[2] == member_type and item[4] == member_name and item[6]
]
exact = next((item for item in declarations if item[5] == descriptor), None)
if exact is not None:
return exact
if not descriptor and declarations:
return declarations[0]
return None


def linkable_member_spans(references, available_classes):
return [
(item[0], item[1])
for item in references
if item[3] in available_classes
]


def identifier_occurrences_in_range(text : str, start : int, end : int, name : str):
spans = []
state = "code"
Expand Down
21 changes: 21 additions & 0 deletions droidasc/asc_client/gui/text_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,24 @@ def decode_java_unicode_escapes(text : str):
if pending_high is not None:
out.append(pending_high_raw)
return "".join(out)


def decode_java_unicode_escapes_with_ranges(text : str, ranges):
if not ranges or "\\u" not in text:
return decode_java_unicode_escapes(text), list(ranges)

points = sorted({point for item in ranges for point in item[:2]})
mapped = {}
source_pos = 0
decoded_pos = 0
for point in points:
decoded_pos += len(decode_java_unicode_escapes(text[source_pos:point]))
mapped[point] = decoded_pos
source_pos = point

decoded = decode_java_unicode_escapes(text)
adjusted = [
(mapped[item[0]], mapped[item[1]], *item[2:])
for item in ranges
]
return decoded, adjusted
2 changes: 2 additions & 0 deletions droidasc/asc_client/gui/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class EditorTab:
xview : tuple = (0.0, 1.0)
insert_index : str = "1.0"
comments : dict = field(default_factory=dict)
member_references : list = field(default_factory=list)
rendered_member_references : list = field(default_factory=list)


class EditorTabBar(ttk.Frame):
Expand Down
14 changes: 11 additions & 3 deletions droidasc/asc_core/findrefs/locator/field_locator.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,12 @@ def _collect_clz_fids(self, type_idxs) -> set:
ret.update(fids)
return ret

def _match_clz_fids(self, clz_fids : set, field : str) -> set:
def _match_clz_fids(self, clz_fids : set, field : str, precise : bool = False) -> set:
ret = set()
fields = self.dex.fields
for fid in clz_fids:
if fields[fid].name.find(field) != -1:
matches = fields[fid].name == field if precise else field in fields[fid].name
if matches:
ret.add(fid)
return ret

Expand All @@ -91,6 +92,9 @@ def locate(self, find : dict) -> set:

clz = find.get("class")
field = find.get("field")
field_precise = False
if isinstance(field, (list, tuple)):
field, field_precise = field
clz_precise = True
if clz is not None:
clz, clz_precise = clz
Expand All @@ -103,6 +107,8 @@ def locate(self, find : dict) -> set:
return set()

if clz is None:
if field_precise:
return {fid for fid, item in enumerate(self.dex.fields) if item.name == field}
name_idxs = self.str_locator.locate(field)
field_maps = self.field_maps
ret = set()
Expand Down Expand Up @@ -133,10 +139,12 @@ def locate(self, find : dict) -> set:
self._debug_log("locate", t_start, len(clz_fids))
return clz_fids
if clz_precise:
ret = self._match_clz_fids(clz_fids, field)
ret = self._match_clz_fids(clz_fids, field, field_precise)
self._debug_log("locate", t_start, len(ret))
return ret

if field_precise:
return self._match_clz_fids(clz_fids, field, True)
name_idxs = self.str_locator.locate(field)
ret = set()
for name_idx in name_idxs:
Expand Down
14 changes: 11 additions & 3 deletions droidasc/asc_core/findrefs/locator/method_locator.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,12 @@ def _collect_clz_mids(self, type_idxs) -> set:
ret.update(mids)
return ret

def _match_clz_mids(self, clz_mids : set, method : str) -> set:
def _match_clz_mids(self, clz_mids : set, method : str, precise : bool = False) -> set:
ret = set()
methods = self.dex.methods
for mid in clz_mids:
if methods[mid].name.find(method) != -1:
matches = methods[mid].name == method if precise else method in methods[mid].name
if matches:
ret.add(mid)
return ret

Expand All @@ -90,6 +91,9 @@ def locate(self, find : dict) -> set:

clz = find.get("class")
method = find.get("method")
method_precise = False
if isinstance(method, (list, tuple)):
method, method_precise = method
clz_precise = True
if clz is not None:
clz, clz_precise = clz
Expand All @@ -102,6 +106,8 @@ def locate(self, find : dict) -> set:
return set()

if clz is None:
if method_precise:
return {mid for mid, item in enumerate(self.dex.methods) if item.name == method}
name_idxs = self.str_locator.locate(method)
method_maps = self.method_maps
ret = set()
Expand Down Expand Up @@ -132,10 +138,12 @@ def locate(self, find : dict) -> set:
self._debug_log("locate", t_start, len(clz_mids))
return clz_mids
if clz_precise:
ret = self._match_clz_mids(clz_mids, method)
ret = self._match_clz_mids(clz_mids, method, method_precise)
self._debug_log("locate", t_start, len(ret))
return ret

if method_precise:
return self._match_clz_mids(clz_mids, method, True)
name_idxs = self.str_locator.locate(method)
ret = set()
for name_idx in name_idxs:
Expand Down
Loading
Loading