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

another possible and portable way to instantiate a class #23

Closed
henri-tremblay opened this issue Apr 8, 2015 · 15 comments
Closed

another possible and portable way to instantiate a class #23

henri-tremblay opened this issue Apr 8, 2015 · 15 comments
Assignees
Milestone

Comments

@henri-tremblay
Copy link
Contributor

Original issue 20 created by henri-tremblay on 2013-08-16T08:01:07.000Z:

If the original class is not final you can create a class that extends it, creating direct JVM byte-code, and loading the created class. Then creating an instance of the extended class will invoke the constructor of the extending class, but since the JVM code is generated calling the superconstructor can be skipped (simply not generating the code into the constructor that invokes super).

You may perhaps even create the extending class if the original class is final accessing the original class object already loaded and altering the "final" modifier using reflection.

@henri-tremblay
Copy link
Contributor Author

Comment #1 originally posted by henri-tremblay on 2013-10-06T10:55:40.000Z:

Hi, that sounds interesting. Would be able to provide a patch containing this instantiator?

@henri-tremblay
Copy link
Contributor Author

Comment #2 originally posted by henri-tremblay on 2013-10-06T11:47:18.000Z:

No unfoirtunately I am not, having no time to devote to that.

If you look at the disassembly of a class file, for example:

public javax0.casuar.Node(java.lang.String);
flags: ACC_PUBLIC
Code:
stack=4, locals=3, args_size=2
0: aload_0
1: invokespecial # 75 // Method java/lang/Object."":()V
4: aload_0
5: aconst_null
6: putfield # 59 // Field file:Ljava/io/File;
9: aload_0
10: aconst_null
11: putfield # 36 // Field subdirs:[Ljava/io/File;
14: aload_0
15: aconst_null
16: putfield # 42 // Field children:[Ljavax0/casuar/Node;
19: aload_1
20: ifnonnull 32
23: ldc # 76 // String user.home
25: invokestatic # 78 // Method java/lang/System.getProperty:(Ljava/lang/String;)Ljava/lang/String;
28: astore_2
29: goto 34
32: aload_1
33: astore_2
34: aload_0
35: new # 45 // class java/io/File
38: dup
39: aload_2
40: invokespecial # 84 // Method java/io/File."":(Ljava/lang/String;)V
43: putfield # 59 // Field file:Ljava/io/File;
46: new # 85 // class javax0/casuar/Node$SubdirCollector
49: dup
50: aload_0
51: dup
52: invokevirtual # 87 // Method java/lang/Object.getClass:()Ljava/lang/Class;
55: pop
56: aconst_null
57: invokespecial # 91 // Method javax0/casuar/Node$SubdirCollector."":(Ljavax0/casuar/Node;Ljavax0/casuar/Node$SubdirCollector;)V
60: invokevirtual # 94 // Method javax0/casuar/Node$SubdirCollector.start:()V
63: return

You can see that the invocatio of the super() is explicitly coded into the file as

     0: aload_0       
     1: invokespecial #&nbsp;75                 // Method java/lang/Object."<init>":()V

If you create the JVM code using asm tools, like cglib does then you can skip this.

@henri-tremblay
Copy link
Contributor Author

Comment #3 originally posted by henri-tremblay on 2013-10-06T11:52:35.000Z:

Wouldn't the JVM's bytecode verifier reject the class?

@henri-tremblay
Copy link
Contributor Author

Comment #4 originally posted by henri-tremblay on 2013-10-06T11:55:53.000Z:

One should give a try. My bet is that it will not.

@henri-tremblay
Copy link
Contributor Author

Comment #5 originally posted by henri-tremblay on 2013-10-06T12:15:25.000Z:

Have you tried?

I would be surprised if the bytecode verifier did not reject the class when the classloader defined the class from the invalid bytecode.

@henri-tremblay
Copy link
Contributor Author

Indeed, just removing the constructor of the super class causes a VerifyError. I will now try to call the constructor of Object.

@henri-tremblay
Copy link
Contributor Author

In fact, it does work when -Xverify:none is used

@henri-tremblay henri-tremblay self-assigned this Jul 23, 2015
@henri-tremblay henri-tremblay added this to the 2.2 milestone Jul 23, 2015
@jcarvalho
Copy link

A similar approach to this one is to synthesize the ObjectInstantiator instance, so it does what you attempted (manually invoking the desired constructor), and having it extend sun.reflect.MagicAccessorImpl, as its subclasses are not subject to the regular bytecode verification, and allow you to do it without globally disabling the verifier.

I have a working prototype of this, and despite not having tested it in any platform other than Hotspot, it seems to work great.

@jcarvalho
Copy link

(I know this is not portable, just wanted to share another approach that I haven't seen explored yet)

@henri-tremblay
Copy link
Contributor Author

MagicAccessorImpl is package scope. You generate the ObjectInstantiator? Can you share the prototype in a gist or github repo?

@henri-tremblay
Copy link
Contributor Author

Objenesis isn't supposed to have portable instantiators. It is supposed to work on any platform. Right now, the only none working platform I am aware of if Google App Engine.

@jcarvalho
Copy link

MagicAccessorImpl is package scope yes, however, its 'no verifier' property means you can create subclasses outside of the package.

You can find the prototype here. The interesting part is in the MagicInstantiatorStrategy class.

I am using ASM to generate the ObjectInstantiator instance. This is a relatively performance-heavy operation, but seeing as instantiator instances are cached by default, this should provide performance similar to the other instantiators.

@henri-tremblay
Copy link
Contributor Author

How cute. You can generate a class extending it but not extend it from source code :-)

Thanks for the code. I will try it ASAP.

Can you tell me why or on which platform we are using it? (so why the classical instantiators where not working)

@jcarvalho
Copy link

I wrote this when searching for an alternative to Sun's ReflectionFactory, first attempted something similar to what you attempted in this issue, and stumbled upon the 'magic' solution.

It was only when I saw you attempting this and failing that I ported it to Objenesis. It will probably only work on the same platforms as ReflectionFactory, it is simply another approach.

@henri-tremblay
Copy link
Contributor Author

I've forked your idea in a issue #35

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

2 participants