--- tags: decompiler title: Other Errors --- # Other errors | Other Error | Count| Status| | ------- | ----| ----| | Reading file error (throw) | 3 | :heavy_check_mark: | | Corrupted files (throw) | 209 |:heavy_check_mark: | | Tokenization errors (throw) | 129| :warning: | | Parse errors w/o .py (explicit) | 267 |:heavy_check_mark: | | Internal grammar rule-bug (explicit) | 844|:heavy_check_mark: | | Python 3.9 | 21050 - (3045) - 840 | :warning: | Break down of other errors: 1) **Reading file error** (3): Trivial since the file was deleted by antivirus on my end (my bad) 2) **Bytecode is corrupted** (209): This is actually corrupted files confirmed via dis 3) **Conversion to their own tokens**(129): These are files that have dis success but because of some patterns that the decompiler expects, they fail. -> Theoretically can be fixed but will take alot more time since would need to instrument in decompiler to find the locations. The positions in code that it fails also varies but are all in the same stage of tokenization. 4) **Parse Errors without py output**(267): These are those that are indeed parse errors but do not have .py output except have filenames printed. Relatively easier to fix that previous one since filenames are there so can look up source files 5) **Internal grammar rule-bug**(844): These are those with py output but fail while finalizing the final python code. Effectively still don't point to where they fail and would be similar to implicit errors. 6) **Parse Errors that we focus on**(11029): These are the ones we are working on and this is the final level of error 7) **Python 3.9 code** (21050): These are also we are already covering ## Corrupted files All files were run under dis to confirm whether they were corrupted or not. Furthermore, the headers were changed to different python versions and `dis` was re-tried to see if there were header errors but there was no success. ## `Tokenization errors` ### Key error - out of 126: - 123 (all python 3.8): `jump_back_index = self.offset2tok_index[jump_target] - 1` - [ref](https://github.com/rocky/python-uncompyle6/blob/c7ebdb344be0ceb938b764b6b81a6a3af9913f27/uncompyle6/scanners/scanner38.py#L108) - 6 (all python 2.7): `j = self.offset2inst_index[offset]` - [ref](https://github.com/rocky/python-uncompyle6/blob/f6f0e344d02925630e4b5c78a36ef8144dd78938/uncompyle6/scanners/scanner2.py#L400) ### Other tokenizatione errors: - `AssertionError: at ifpoplaststmtl[2], expected 'c_stmts' node; got 'pass'` : 21 - `IndexError: list index out of range` : 11 - `AssertionError: set_comp_func (8)` : 2 - `IndexError: pop from empty list` : 2 All of these fall in code errors. ## `Internal grammar error` ### Common trends - All have errors on `break` statement. - All cause errors in python 3.8 bytecode - Total of 844 - All have py output ### Summary trends - 1. `while` loop is nested in `elif` block and has `break` - 2. `while` loop has `1` or any constant value as condition and has `break` - 3. Multiple conditions and nested `for` loops and has `break` in external loop - Does not loose any loops - 4. `for` loop is nested in `elif` block and has `break` - 5. Large codebase with multiple loops and one `pass` in `elif` block causes the decompiler to add `break` - 6. `for` loop with 2 `continue` and one `break` in `else` block of `try/except/else` - 7. Implicit error leads to pattern 1 - 8. Just multiple `for` loops in `elif` ### Examples Example 1: ```python def _task_get_stack(task, limit): if a: z=z#frames.reverse() elif b: while tb is not None: if limit <= 0: break ``` output: ```python def _task_get_stack(task, limit): if a: z = z else: if b: if tb is not None: if limit <= 0: break # NOTE: have internal decompilation grammar errors. # Use -t option to show full context. # not in loop: # break # L. 10 30 BREAK_LOOP 34 'to 34' ``` Solution: Using transformation key_word ```python def _task_get_stack(task, limit): if a: z=z#frames.reverse() elif b: while tb is not None: if limit <= 0: FET_break() ``` or break `elif` ```python= def _task_get_stack(task, limit): if a: z=z#frames.reverse() if b and not a: while tb is not None: if limit <= 0: break ``` Example 2: ```python def _fix_exception_context(new_exc, old_exc): # Context may not be correct, so find the end of the chain while 1: exc_context = new_exc.__context__ if exc_context is old_exc: # Context is already set correctly (see issue 20317) return if exc_context is None or exc_context is frame_exc: break new_exc = exc_context # Change the end of the chain to point to the exception # we expect it to reference new_exc.__context__ = old_exc ``` output: ```python def _fix_exception_context(new_exc, old_exc): exc_context = new_exc.__context__ if exc_context is old_exc: return else: if not exc_context is None: if exc_context is frame_exc: break new_exc = exc_context new_exc.__context__ = old_exc # NOTE: have internal decompilation grammar errors. # Use -t option to show full context. # not in loop: # break # L. 9 34 BREAK_LOOP 42 'to 42' ``` solution ```python def _fix_exception_context(new_exc, old_exc): # Context may not be correct, so find the end of the chain tmp = 1 while tmp: exc_context = new_exc.__context__ if exc_context is old_exc: # Context is already set correctly (see issue 20317) return if exc_context is None or exc_context is frame_exc: break new_exc = exc_context # Change the end of the chain to point to the exception # we expect it to reference new_exc.__context__ = old_exc ``` > Note: This leads to another implicit error but will count that as another error. Example 3: ```python= def parse_parts(self, parts): parsed = [] sep = self.sep altsep = self.altsep drv = root = '' it = reversed(parts) for part in it: if not part: continue if altsep: part = part.replace(altsep, sep) drv, root, rel = self.splitroot(part) if sep in rel: for x in reversed(rel.split(sep)): if x and x != '.': parsed.append(sys.intern(x)) else: if rel and rel != '.': parsed.append(sys.intern(rel)) if drv or root: if not drv: for part in it: if not part: continue if altsep: part = part.replace(altsep, sep) drv = self.splitroot(part)[0] if drv: break break if drv or root: parsed.append(drv + root) parsed.reverse() return drv, root, ``` Example 4: ```python= def find_module(self, fullname, path=None): if fullname in self.toc: z=z elif path is not None: z=z for p in path: if not p.startswith(SYS_PREFIX): continue p = p[SYS_PREFIXLEN:] parts = p.split(pyi_os_path.os_sep) if not parts: continue if entry_name in self.toc: break return module_loader ``` output: ```python def find_module(self, fullname, path=None): if fullname in self.toc: z = z else: if path is not None: z = z for p in path: if not p.startswith(SYS_PREFIX): pass else: p = p[SYS_PREFIXLEN:] parts = p.split(pyi_os_path.os_sep) if not parts: pass elif entry_name in self.toc: break return module_loader ``` solution: ```python def find_module(self, fullname, path=None): if fullname in self.toc: z=z if path is not None and not fullname in self.toc: z=z for p in path: if not p.startswith(SYS_PREFIX): continue p = p[SYS_PREFIXLEN:] parts = p.split(pyi_os_path.os_sep) if not parts: continue if entry_name in self.toc: break return module_loader ``` Example 5: [Link](https://github.com/numpy/numpy/blob/main/numpy/lib/arraypad.py#L806) Solution: No solution Example 6: ``` def process_listeners(self, listener_type, argument, result): removed = [] for i, listener in enumerate(self._listeners): if listener.type != listener_type: continue future = listener.future if future.cancelled(): removed.append(i) continue try: passed = listener.predicate(argument) except Exception as exc: future.set_exception(exc) removed.append(i) else: if passed: future.set_result(result) removed.append(i) if listener.type == ListenerType.chunk: break ``` Output => ``` XXXX anything loses information in decompiler. example: def process_listeners(self, listener_type, argument, result): removed = [] for i, listener in enumerate(self._listeners): if listener.type != listener_type: continue future = listener.future if future.cancelled(): removed.append(i) continue try: passed = listener.predicate(argument) except Exception as exc: future.set_exception(exc) removed.append(i) else: if passed: future.set_result(result) removed.append(i) if listener.type == ListenerType.chunk: tmp = 'break' if tmp=='break': break ``` Example 7: ```python def determineEncoding(self, chardet=True): # "likely" encoding charEncoding = lookupEncoding(self.likely_encoding), "tentative" if charEncoding[0] is not None: return charEncoding # Guess with chardet, if available if chardet: try: from chardet.universaldetector import UniversalDetector except ImportError: pass else: buffers = [] detector = UniversalDetector() while not detector.done: buffer = self.rawStream.read(self.numBytesChardet) assert isinstance(buffer, bytes) if not buffer: break buffers.append(buffer) detector.feed(buffer) detector.close() encoding = lookupEncoding(detector.result['encoding']) self.rawStream.seek(0) if encoding is not None: return encoding, "tentative" # Try the default encoding ``` converts to => ```python def determineEncoding(self, chardet=True): charEncoding = ( lookupEncoding(self.likely_encoding), 'tentative') if charEncoding[0] is not None: return charEncoding elif chardet: try: from chardet.universaldetector import UniversalDetector except ImportError: pass else: buffers = [] detector = UniversalDetector() if not detector.done: buffer = self.rawStream.read(self.numBytesChardet) assert isinstance(buffer, bytes) if not buffer: break buffers.append(buffer) detector.feed(buffer) else: detector.close() encoding = lookupEncoding(detector.result['encoding']) self.rawStream.seek(0) if encoding is not None: return ( encoding, 'tentative') ``` `elif chardet:` makes into pattern 1 and so causing issues Example 8: ``` def tokens(self, event, next): kind, data, _ = event if kind == START: tag, attribs = data name = tag.localname namespace = tag.namespace converted_attribs = {} for k, v in attribs: if isinstance(k, QName): converted_attribs[(k.namespace, k.localname)] = v else: converted_attribs[(None, k)] = v if namespace == namespaces["html"] and name in voidElements: for token in self.emptyTag(namespace, name, converted_attribs, not next or next[0] != END or next[1] != tag): yield token else: yield self.startTag(namespace, name, converted_attribs) elif kind == END: name = data.localname namespace = data.namespace if namespace != namespaces["html"] or name not in voidElements: yield self.endTag(namespace, name) elif kind == COMMENT: yield self.comment(data) elif kind == TEXT: for token in self.text(data): yield token elif kind == DOCTYPE: yield self.doctype(*data) elif kind in (XML_NAMESPACE, DOCTYPE, START_NS, END_NS, START_CDATA, END_CDATA, PI): pass else: yield self.unknown(kind) ``` Causes: ``` def tokens(self, event, next): kind, data, _ = event if kind == START: tag, attribs = data name = tag.localname namespace = tag.namespace converted_attribs = {} for k, v in attribs: if isinstance(k, QName): converted_attribs[(k.namespace, k.localname)] = v else: converted_attribs[(None, k)] = v else: if namespace == namespaces['html'] and name in voidElements: for token in self.emptyTag(namespace, name, converted_attribs, not next or next[0] != END or next[1] != tag): yield token else: yield self.startTag(namespace, name, converted_attribs) else: if kind == END: name = data.localname namespace = data.namespace if namespace != namespaces['html'] or name not in voidElements: yield self.endTag(namespace, name) else: if kind == COMMENT: yield self.comment(data) else: if kind == TEXT: for token in self.text(data): yield token else: if kind == DOCTYPE: yield (self.doctype)(*data) else: if kind in (XML_NAMESPACE, DOCTYPE, START_NS, END_NS, START_CDATA, END_CDATA, PI): break else: yield self.unknown(kind) # NOTE: have internal decompilation grammar errors. # Use -t option to show full context. # not in loop: # break # L. 40 354 BREAK_LOOP 368 'to 368' ```