Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get isinstance to return reasonable results for BridgedObjects for type, callable, etc #32

Open
fmagin opened this issue Nov 9, 2019 · 2 comments

Comments

@fmagin
Copy link
Contributor

fmagin commented Nov 9, 2019

I am currently working on this but it is in no shape for a PR yet.

The idea is that BridgedCallables should behave as close as possible to actual local Callables to allow the inspect features and IPython features building on top of that to work as good as possible.

The concrete goal is the following:
Assume a function like:

def add(x: int, y:int ) -> int:
    return x + y

IPython help returns the following.

In [34]: add?
Signature: add(x: int, y: int) -> int
Docstring: <no docstring>
File:      ~/Projects/ghidra_bridge/dev.py
Type:      function

This behavior should be replicated with BrigdedCallables. What is needed for this is that is after several layers of IPython code the inspect module can generate a valid signature for it by using Signature.from_callable. This currently fails with the following:

from inspect import Signature
f = currentProgram.functionManager.getFunctionAt
Signature.from_callable(f)   
ValueError                                Traceback (most recent call last)
<ipython-input-36-6e450cf1e523> in <module>
----> 1 Signature.from_callable(f)

/usr/lib64/python3.7/inspect.py in from_callable(cls, obj, follow_wrapped)
   2831         """Constructs Signature for the given callable object."""
   2832         return _signature_from_callable(obj, sigcls=cls,
-> 2833                                         follow_wrapper_chains=follow_wrapped)
   2834 
   2835     @property

/usr/lib64/python3.7/inspect.py in _signature_from_callable(obj, follow_wrapper_chains, skip_bound_arg, sigcls)
   2286     if _signature_is_builtin(obj):
   2287         return _signature_from_builtin(sigcls, obj,
-> 2288                                        skip_bound_arg=skip_bound_arg)
   2289 
   2290     if isinstance(obj, functools.partial):

/usr/lib64/python3.7/inspect.py in _signature_from_builtin(cls, func, skip_bound_arg)
   2110     s = getattr(func, "__text_signature__", None)
   2111     if not s:
-> 2112         raise ValueError("no signature found for builtin {!r}".format(func))
   2113 
   2114     return _signature_fromstr(cls, func, s, skip_bound_arg)

ValueError: no signature found for builtin <BridgedCallable('<bound method ghidra.program.database.function.FunctionManagerDB.getFunctionAt of ghidra.program.database.function.FunctionManagerDB@5f0ef8c4>', type=instancemethod, handle=99f4707d-9f4a-4205-b820-7dac1b5a811b)>

The first hint is that no signature found for builtin is weird because a BridgedCallable is in no way a builtin so something is going quite wrong.

The first divergence from the Jython shell is that in the Jython shell isinstance(f, types.MethodType) is True while in the bridge client it is false. I am unsure how to fix this exactly as isinstance is a builtin that might be hard to trick.

An alternative to bypass this all is to fake __signature__ directly and just build one ourselves. Slightly annoying and ignores the potential actual problem.

It gets recognized as a builtin because ismethoddescriptor returns True, which in happens because ismethod returns False for currentProgram.functionManager.getFunctionAt

One core issue is: Is there some way to make isinstance(obj, type) go over to the bridge and is that even the correct way to do it?
I will look into how other environments like rpyc and Jython handle this and might have more concrete ideas then.

@fmagin
Copy link
Contributor Author

fmagin commented Nov 9, 2019

Turns out building our own Signature is fairly easy, so I don't quite care about isinstance working for all those types. I'll leave this issue open in case someone else runs into this, but won't work on it.

@fmagin
Copy link
Contributor Author

fmagin commented Nov 22, 2019

More or less accidentally gathered results: If hacking e.g. inspect.isclass to recognize BridgedObjects of Java Classes things break in IPython anyway.

One possible solution to fix isinstance in general though might be overriding the builtin (which I think works and should get picked up by the inspect module). Currently the builtin is used in https://github.com/justfoxing/ghidra_bridge/blob/master/ghidra_bridge/bridge.py#L985-L997 so that code would need to be changed to save the original version once and then change it.

@fmagin fmagin changed the title Make inspection of BridgedCallables as transparent as possible Get isinstance to return reasonable results for BridgedObjects for type, callable, etc Nov 22, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant