-
Notifications
You must be signed in to change notification settings - Fork 133
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
Allow passing arguments to superclass constructor in static proxy #10
Comments
I need this feature. I am subclassing IntentService and need to call super constructor of IntentService |
For IntentService your constructor parameter is probably always the same, in which case you can work around this by inheriting indirectly. For example, create the following Java class:
And then inherit that in Python:
|
Very few people are using the static proxy feature, so we won't be doing this in the foreseeable future. |
Old notes from 2018: We cannot simply generate forwarding constructors for each public constructor in the base class. IntentService is an example of where a 1-argument superclass constructor must be called by a zero-argument subclass constructor. (Though in this case you can probably work around by inheriting via an intermediate Java class: see example here.) Java syntax is very restrictive:
The JVM spec is a bit more flexible:
Jython takes advantage of this to allow standard Python syntax. It ensures the bytecode passes the verifier by generating a zero-argument "super()" call at the end of all paths which don't otherwise contain one. However, Jython works by translating the Python source into bytecode, which will never be our approach. We could bypass the bytecode verifier if we could generate a native constructor, and then call the base class constructor from Cython code using CallNonVirtual. However, native constructors aren't allowed in Java source code, so this could only be done using bytecode generation. I don't see anything in the JVM or DEX specs prohibiting native constructors, but I can't find any reference to anyone attempting them either, so I have no confidence that they'll work. For standard Java bytecode, commonly used generator tools include cglib and javassist. This question also mentions Byte Buddy as having DEX support, and there's a DEX-only tool called dexmaker. Dynamic loading of .dex files does appear to work:
But to make this work for classes loaded outside of our control (e.g. Activity or View subclasses), we'd have to modify or replace the ClassLoader used by that code. This is platform and application-specific, and can only be done by modifying internal data structures. For example, to change the ClassLoader used to load Activities, you'd have do something like this, which uses reflection to do the equivalent of: context.mMainThread.mPackages.get(context.getPackageName()).mClassLoader = <new loader> But using reflection to access private members of Android platform classes is now mostly blocked. To support base classes like IntentService, I was initially thinking of something like this:
where the quoted string would be copied directly into the generated Java constructor with no further interpretation. We could also support a "this" keyword which would work the same way. VOC used a similar approach: https://github.com/beeware/voc/blob/90fc07386bae942b7eb499ba98792fb5c75d8121/voc/python/ast.py#L1008-L1025 However, we really want to use the standard Python syntax if at all possible. The difficulty with this comes with Java-initiated construction. Even with bytecode generation, JVM verification means that the constructor must contain a super() call; we can't do that via JNI unless we can make a native constructor (notes above). And if using source code generation, the super constructor signature must be known at compile time: we can't use run-time overload resolution here. The best approach I've thought of so far is:
The exception is necessary because If the Java constructor waited for the Python constructor to return normally before doing the Java super() call, the remainder of the Python constructor wouldn't have access to a valid Java object, so it couldn't do much useful. (If the super() call is zero-argument, which is the default, then the initial How do we identify this object if we can't access any local variables including
The double call to Other possible ways of achieving this without the double call (haven't thought these through):
For Python-initiated construction the Python constructor would only be called once, and the Java object would be created when
The result is that we get almost normal Python syntax and semantics from the Python |
Toga only requires Python-intiated construction, which is the easier case to solve, so we should look at that first. However, the current workaround of putting a Canvas helper class in the Briefcase Gradle template works fine, so this isn't urgent. |
I also need this feature for extending ArrayAdapter (see beeware/toga#1393) or EditText (for adding line numbering). I know that we could add these extended classes to the briefcase Android template, but we will end up with lots of those classes and the dependencies between the 2 projects will increase the complexity unnecessarily |
@mhsmith Sounds good to me! I currently only need a solution for Toga.
Well, Russel just closed my PR beeware/briefcase-android-gradle-template#61 which used this workaround. So, the workaround is not really a solution, not even an intermediate one :-( I would really appreciate if you could make this subclassing work for Toga. I would love to offer my help, but this stuff is beyond my knowledge. |
Like Russell said, our current priority is to complete and test the existing Toga APIs, so unless this issue turns out to block something more critical, I probably won't have time to look at it in the next few months. Sorry about that. |
In Toga, this would currently be useful for:
|
As it says in the documentation, "Supplying arguments to the superclass constructor is not currently possible" in a
static_proxy
. This prevents them inheriting directly from Java classes which don't have a zero-argument constructor, such as View and IntentService.We may fix this in the future, but it's a difficult technical problem, and it seems that relatively few users are interested in the static_proxy feature anyway.
If this limitation affects anyone else, please click the thumbs up button below, and subscribe to this issue.
The text was updated successfully, but these errors were encountered: