0

I've just updated my j2objc tool from 1.0.2 to 2.8. As far as I know there were changes in reflection starting from 1.1 version. And now code doesn't work anymore.

We have IApplicationSupport.java interface. Something like this:

public interface IApplicationSupport
{
    void openSupport();
}

This code is successfully translated to Objective-С with j2objc

Then we have platform dependent classes AndroidApplicationSupport and IosApplicationSupport that are not translated. Android part is ok, but iOS has some problems.

Somewhere in code later I need to call [IOSClass classForIosName:@"IosApplicationSupport"]. And that's the problem. I get the next exception every time com.google.j2objc.ReflectionStrippedError: IosApplicationSupport: Reflection is unavailable. Fix this by avoiding reflection or building without --strip-reflection. Of course I do not use --strip-reflection flag during translation (but I believe the problem is not there). Avoid reflection seems to be extremely hard in my case, because there are too much classes with reflection are used.

I understand, that translated classes has rather big __metadata method. Is there any way to emulate metadata for native classes to enable reflection support? If I have to add metadata, are there any rules about how is this generated? Our project is heavy depends on it.

Thanks for any help!

Accid Bright
  • 592
  • 5
  • 17

1 Answers1

0

Sure, the __metadata method can be easily added. The easiest way is to create a simple stub file, which defines the package, class, and method, then translate it once and copy the generated method into your native class. Here's an example stub for IosApplicationSupport.java (I had to guess what package IApplicationSupport is in):

import foo.bar.IApplicationSupport;

public class IosApplicationSupport implements IApplicationSupport {
  public void openSupport() {
  }
}

After running j2objc on this class, its metadata is:

+ (const J2ObjcClassInfo *)__metadata {
  static J2ObjcMethodInfo methods[] = {
    { NULL, NULL, 0x1, -1, -1, -1, -1, -1, -1 },
    { NULL, "V", 0x1, -1, -1, -1, -1, -1, -1 },
  };
  #pragma clang diagnostic push
  #pragma clang diagnostic ignored "-Wobjc-multiple-method-names"
  #pragma clang diagnostic ignored "-Wundeclared-selector"
  methods[0].selector = @selector(init);
  methods[1].selector = @selector(openSupport);
  #pragma clang diagnostic pop
  static const J2ObjcClassInfo _IosApplicationSupport = { "IosApplicationSupport", NULL, NULL, methods, NULL, 7, 0x1, 2, 0, -1, -1, -1, -1, -1 };
  return &_IosApplicationSupport;
}

(That looks huge, but it's just 128 bytes on a 64-bit processor (NSLog(@"sizeof metadata: %ld", sizeof(_IosApplicationSupport) + sizeof(methods));).)

However, metadata isn't necessary if you're just loading a class and creating a default instance of it, as the Objective-C runtime directly supports that. Since you didn't say what package IApplicationSupport is in, using a foo.bar package this code successfully executes without metadata:

  IOSClass *appSupportClass = [IOSClass classForIosName:@"IosApplicationSupport"];
  id<FooBarIApplicationSupport> appSupport = [appSupportClass newInstance];
  [appSupport openSupport];

Since class loading and instance creation works, it sounds like IosApplicationSupport uses the java.lang.reflect API, which requires metadata. However, since 1.0.2 there's an easier way than hand-coding IosApplicationSupport: use OCNI. OCNI allows you to embed Objective-C directly into Java native methods. We use this a lot in the JRE emulation library, such as here, where native iOS MD5 signing is done in a Java class. This avoids having to define and maintain a metadata struct, just code in Java and insert Objective-C where it's necessary.

So my recommendation is to rewrite IosApplicationSupport in Java, and define native Java methods with OCNI where you need native code.

tball
  • 1,984
  • 11
  • 21
  • Thanks, @tball for your explanation, it is much clear to me now. I understand your ideas but still don't find suitable solution for us. `classForIosName:` really works well, the problem is that deeper in stack we call `[class getConstructors]` which throws exception. We have our own implementation of Dependency Injection pattern and when it searches constructor for object it calls the method above. So I need to search something else to solve... – Accid Bright Oct 20 '21 at 17:38
  • 1
    Good, that means everything is working correct, since getConstructors is one of the many reflection methods that require metadata. To generate metadata without sweating over all its specifics, just ceate a Java stub file of your IosApplicationSupport, run j2objc with it, then cut&paste the __metadata class method into your native source file. For stub file examples, see: https://github.com/google/j2objc/tree/master/jre_emul/stub_classes/java – tball Oct 21 '21 at 21:17