Home » Language IDEs » Java Development Tools (JDT) » Eclipse compiler and static initializers
Eclipse compiler and static initializers [message #243287] |
Mon, 30 April 2007 04:50  |
Eclipse User |
|
|
|
Hello,
I'm currently developing a complex app with Groovy and I've run into a
strange problem which the compiler developers in the JDK must have solved.
I have a class Foo:
class Foo
{
public Foo () { throw new RuntimeException ("Go away!");
}
class StaticInitBug
{
private final static Foo foo = new Foo ();
}
class Bar
{
void x ()
{
StaticInitBug dummy = new StaticInitBug ();
}
}
The problem: When Bar is compiled, the class StaticInitBug is loaded and
resolved (actually, Groovy uses Reflection here which does the loading and
resolving) which causes the static initializer to be invoked -> compiler
crashes because of the RuntimeException.
Apparently, this doesn't happen in the Eclipse Java compiler. How do you
load classes to check for missing symbols when Java Reflection can't be
used?
Regards,
Aaron
|
|
|
Re: Eclipse compiler and static initializers [message #243301 is a reply to message #243287] |
Mon, 30 April 2007 12:41   |
Eclipse User |
|
|
|
Originally posted by: wharley.bea.com
"Aaron Digulla" <digulla@hepe.com> wrote in message
news:a9149246c184478608f5e5b0cca5dee3$1@www.eclipse.org...
> [...]
>
> The problem: When Bar is compiled, the class StaticInitBug is loaded and
> resolved (actually, Groovy uses Reflection here which does the loading and
> resolving) which causes the static initializer to be invoked -> compiler
> crashes because of the RuntimeException.
>
> Apparently, this doesn't happen in the Eclipse Java compiler. How do you
> load classes to check for missing symbols when Java Reflection can't be
> used?
The Eclipse Java compiler, like most Java compilers, reads .java files, and
it reads .class files if necessary to get information about already-compiled
classes. To get a sense of how it thinks about code, take a look at the
classes in org.eclipse.jdt.internal.compiler.*, in the org.eclipse.jdt.core
plug-in. It doesn't use reflection, and it doesn't load classes at all
(except for the classes that make up its own implementation). The language
of the compiler is unrelated to the language of the code being compiled,
except that they happen to be the same. Indeed, the first Java compilers
were not written in Java and did not run on Java virtual machines; I think
the compiler that eventually became Eclipse started out written in
Smalltalk, although I might be mistaken about that.
Compilers do not in general load user code, for reasons exactly such as the
one you encountered. It's also in general unnecessary and unsafe, not to
mention that it causes bad performance. It would create the potential for
classloader conflicts, if the user code had a class whose name and package
were the same as one of the compiler implementation classes. And in the
case of compiling for a different platform than what you're running on, it
could simply be impossible.
And imagine if, instead of throwing an exception, your code actually *did*
something, like say formatting a hard drive or withdrawing the cesium rods
from the reactor. (Hey, don't laugh, somewhere that code must exist, right?
They don't use a big hand crank.) "Uh, sorry about the meltdown, boss, I
was just trying to compile some test code, and I guess the compiler must
have loaded and executed the classes in the reactor controller jar file."
|
|
| | |
Re: Eclipse compiler and static initializers [message #243363 is a reply to message #243287] |
Wed, 02 May 2007 15:57   |
Eclipse User |
|
|
|
G'Day Aaron,
I don't really understand what you are trying to do and how sophisticated
you want to get, but something to bear in mind is that there is a
distinction between 'loading' a class, and 'initializaing' a class.
In your example an exception is thrown by the class StaticInitBug when that
class is initialized, however it is possible to load the class, without the
exception being thrown.
Depending on the bigger picture of what you are trying to do, your mileage
will vary, because a class that is loaded but not initalized cannot be used,
however you can examine its **public** properties.
In the the example JUnit test case shown below an exception is NOT thrown
due to the class being initialized - however in its current form as shown
below, the test will fail because the call cls.getField( 'foo' ) will throw
a NoSuchMethodExcetion, because in your example StaticInitBug class, the
'foo' field is private - and thus is effectively invisible...
However if for the purposes of experimentation, if you temporarily make the
field 'foo' public, this JUnit test will pass, and will print out the string
"Found field:foo type:Foo".
Whether any of this is useful ot you really depends on what you are
attempting to do.
If all you want to do is introspect the **public** interface of a Class,
then calling Class.forName( className, false, classLoader ) should allow you
load the class without initializating it, and thus introspect the public
interface of the class. However this is probably of little use to you, if
you're trying to do something more sophisticated, such as access the
non-public interface of the class.
Cheers,
- Andy
public class StaticInitBugTest extends TestCase
{
public void testStaticInitBug() throws Exception
{
/* the line below WONT blow up, because the class is NOT initialized.
*/
Class cls =
Class.forName("StaticInitBug",false,this.getClass().getClassLoader());
assertNotNull( cls );
/* the line below will throw a NoSuchMethodException becasue foo is
private */
Field fooField = cls.getField( "foo" );
assertNotNull( fooField );
Field[] fields = cls.getFields();
for ( Field field : fields )
{
// in an actual program you would do something interesting here...
String info = "Found field:" + field.getName() + " type:" +
field.getType().getName();
System.out.println( info );
}
}
}
"Aaron Digulla" <digulla@hepe.com> wrote in message
news:a9149246c184478608f5e5b0cca5dee3$1@www.eclipse.org...
> Hello,
>
> I'm currently developing a complex app with Groovy and I've run into a
> strange problem which the compiler developers in the JDK must have solved.
>
> I have a class Foo:
>
> class Foo
> {
> public Foo () { throw new RuntimeException ("Go away!");
> }
>
> class StaticInitBug
> {
> private final static Foo foo = new Foo ();
> }
>
> class Bar
> {
> void x ()
> {
> StaticInitBug dummy = new StaticInitBug ();
> }
> }
>
> The problem: When Bar is compiled, the class StaticInitBug is loaded and
> resolved (actually, Groovy uses Reflection here which does the loading and
> resolving) which causes the static initializer to be invoked -> compiler
> crashes because of the RuntimeException.
>
> Apparently, this doesn't happen in the Eclipse Java compiler. How do you
> load classes to check for missing symbols when Java Reflection can't be
> used?
>
> Regards,
> Aaron
>
|
|
|
Re: Eclipse compiler and static initializers [message #243367 is a reply to message #243363] |
Wed, 02 May 2007 17:26   |
Eclipse User |
|
|
|
One other detail - if you decide that using reflection on an uninitialized
class does seem the correct mechanism for you to use, you can get around the
limitation that by default reflection only provides access to the public
aspects of a class, by installing a security policy that grants your the
code java.lang.reflect.ReflectPermission suppressAllChecks permission.
ie. You'd need to create a security.policy file something along the lines of
the following.
grant codeBase "file:PATH_TO_YOUR_JAR {
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
}
However you probably want to think twice about what you're doing before
heading down this path.
- Andy
"Andy Carroll" <andy_carroll@hotmail.com> wrote in message
news:f1aqfp$dk4$1@build.eclipse.org...
> G'Day Aaron,
>
> I don't really understand what you are trying to do and how sophisticated
> you want to get, but something to bear in mind is that there is a
> distinction between 'loading' a class, and 'initializaing' a class.
> In your example an exception is thrown by the class StaticInitBug when
> that class is initialized, however it is possible to load the class,
> without the exception being thrown.
> Depending on the bigger picture of what you are trying to do, your mileage
> will vary, because a class that is loaded but not initalized cannot be
> used, however you can examine its **public** properties.
>
> In the the example JUnit test case shown below an exception is NOT thrown
> due to the class being initialized - however in its current form as shown
> below, the test will fail because the call cls.getField( 'foo' ) will
> throw a NoSuchMethodExcetion, because in your example StaticInitBug class,
> the 'foo' field is private - and thus is effectively invisible...
>
> However if for the purposes of experimentation, if you temporarily make
> the field 'foo' public, this JUnit test will pass, and will print out the
> string "Found field:foo type:Foo".
>
> Whether any of this is useful ot you really depends on what you are
> attempting to do.
>
> If all you want to do is introspect the **public** interface of a Class,
> then calling Class.forName( className, false, classLoader ) should allow
> you load the class without initializating it, and thus introspect the
> public interface of the class. However this is probably of little use to
> you, if you're trying to do something more sophisticated, such as access
> the non-public interface of the class.
>
> Cheers,
>
>
> - Andy
>
> public class StaticInitBugTest extends TestCase
> {
> public void testStaticInitBug() throws Exception
> {
> /* the line below WONT blow up, because the class is NOT initialized.
> */
> Class cls =
> Class.forName("StaticInitBug",false,this.getClass().getClassLoader());
> assertNotNull( cls );
>
> /* the line below will throw a NoSuchMethodException becasue foo is
> private */
>
> Field fooField = cls.getField( "foo" );
> assertNotNull( fooField );
>
> Field[] fields = cls.getFields();
> for ( Field field : fields )
> {
> // in an actual program you would do something interesting here...
> String info = "Found field:" + field.getName() + " type:" +
> field.getType().getName();
> System.out.println( info );
> }
> }
> }
>
>
>
>
> "Aaron Digulla" <digulla@hepe.com> wrote in message
> news:a9149246c184478608f5e5b0cca5dee3$1@www.eclipse.org...
>> Hello,
>>
>> I'm currently developing a complex app with Groovy and I've run into a
>> strange problem which the compiler developers in the JDK must have
>> solved.
>>
>> I have a class Foo:
>>
>> class Foo
>> {
>> public Foo () { throw new RuntimeException ("Go away!");
>> }
>>
>> class StaticInitBug
>> {
>> private final static Foo foo = new Foo ();
>> }
>>
>> class Bar
>> {
>> void x ()
>> {
>> StaticInitBug dummy = new StaticInitBug ();
>> }
>> }
>>
>> The problem: When Bar is compiled, the class StaticInitBug is loaded and
>> resolved (actually, Groovy uses Reflection here which does the loading
>> and resolving) which causes the static initializer to be invoked ->
>> compiler crashes because of the RuntimeException.
>>
>> Apparently, this doesn't happen in the Eclipse Java compiler. How do you
>> load classes to check for missing symbols when Java Reflection can't be
>> used?
>>
>> Regards,
>> Aaron
>>
>
>
|
|
|
Re: Eclipse compiler and static initializers [message #243372 is a reply to message #243363] |
Wed, 02 May 2007 19:42   |
Eclipse User |
|
|
|
Actually, calling Class.forName() appears to initialize the class in
question, not just load it. The javadoc for Class.forName() isn't
especially precise about this, but a short test illustrates it:
public class Test {
public static void main(String... args) throws Exception {
Class.forName("Reactor");
}
}
---
public class Reactor {
static {
if (true)
throw new Error("Meltdown!");
}
}
Running Test will cause an Error to be thrown.
Andy Carroll wrote:
> G'Day Aaron,
>
> I don't really understand what you are trying to do and how sophisticated
> you want to get, but something to bear in mind is that there is a
> distinction between 'loading' a class, and 'initializaing' a class.
> In your example an exception is thrown by the class StaticInitBug when that
> class is initialized, however it is possible to load the class, without the
> exception being thrown.
> Depending on the bigger picture of what you are trying to do, your mileage
> will vary, because a class that is loaded but not initalized cannot be used,
> however you can examine its **public** properties.
>
> In the the example JUnit test case shown below an exception is NOT thrown
> due to the class being initialized - however in its current form as shown
> below, the test will fail because the call cls.getField( 'foo' ) will throw
> a NoSuchMethodExcetion, because in your example StaticInitBug class, the
> 'foo' field is private - and thus is effectively invisible...
>
> However if for the purposes of experimentation, if you temporarily make the
> field 'foo' public, this JUnit test will pass, and will print out the string
> "Found field:foo type:Foo".
>
> Whether any of this is useful ot you really depends on what you are
> attempting to do.
>
> If all you want to do is introspect the **public** interface of a Class,
> then calling Class.forName( className, false, classLoader ) should allow you
> load the class without initializating it, and thus introspect the public
> interface of the class. However this is probably of little use to you, if
> you're trying to do something more sophisticated, such as access the
> non-public interface of the class.
>
> Cheers,
>
>
> - Andy
>
|
|
| |
Re: Eclipse compiler and static initializers [message #243382 is a reply to message #243372] |
Thu, 03 May 2007 09:13   |
Eclipse User |
|
|
|
Jess Garms a écrit :
> Actually, calling Class.forName() appears to initialize the class in
> question, not just load it. The javadoc for Class.forName() isn't
> especially precise about this, but a short test illustrates it:
>
> public class Test {
>
> public static void main(String... args) throws Exception {
> Class.forName("Reactor");
> }
> }
This is specified:
Class.forName(String) is equivalent to:
Class.forName(className, true, currentLoader)
see
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html #forName(java.lang.String)
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html #forName(java.lang.String,%20boolean,%20java.lang.ClassLoader)
specifies how to load a class without initializing it.
In 1.5 and above, you can simply right: X.class in your code and this
will no longer initialize the class. The bytecode load can now take a
Constant_Class and this allows to load a class without initializing it.
HTH,
--
Olivier
|
|
| | | |
Re: Eclipse compiler and static initializers [message #243451 is a reply to message #243377] |
Fri, 04 May 2007 12:56  |
Eclipse User |
|
|
|
G'Day again,
Getting back to your original question, about how does eclipse do it, inside
Eclipse is a class called
"org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader" that does what
you want.It parses class files, and exposes an API
for examining the contents. The Eclipse ClassFileEditor (ie. the editor that
is displayed for a class for which you have no source code - uses this
class).
In order for you to play around with it, try doing the following.
1) Import all org.eclipse.jdt.* plugins into your workspace.
ie.
1.1) Import... -> Plug-in Development -> Plug-ins and Fragments
1.2)Choose binary projects with linked content <- this will give you access
to the source code
1.3) Type in the filter org.eclipse.jdt and click 'add' to add all
org.eclipse.jdt projects.
2) Create a new Eclipse Plugin project - i called my example
"classfilereader" - and thus the sample
code below is in a "classfilereader" package.
NOTE: by the way, don't worry you don't really need to make whatever you
are building an Eclipse plugin
I just did it in this example so you have a sample project with the correct
classpath
In a real app you would need to depend on the Eclipse jars, and of course
comply with the Eclipse
license.
3) Open MANIFEST.MF on your new eclipse plugin project, click on the
"dependencies" tab and
declare a dependency on the following projects, The first 3 give you the
compiler stuff - the last one
is so you can run my example JUnit test below.
org.eclipse.jdt
org.eclipse.jdt.core
org.eclipse.jdt.debug
org.junit
4) Create a new JUnit test as per the example shown below.
I've never played with ClassFileReader - so you're on your own from here - I
dont know the anything about using the
class. You can decide for yourself whether this is useful.
All the JUnit test below does it open its own class file and assert it can
find its own private fields...
So just debug the JUnit test and you can see how Eclipse does things.
You're on your own from here..
Good luck!
- Andy
================================
/**
* Example JUnit test - uses the Eclipse ClassFileReader to read its own
class file./
*/
package classfilereader;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import junit.framework.TestCase;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.env.IBinaryField;
/**
* @author acarroll
*
*/
public class MyTest extends TestCase
{
private static int privateInt = 7;
public void testReadClass() throws Exception
{
File f = new File("bin/classfilereader/MyTest.class");
assertTrue( f.exists() );
ClassFileReader myClassReader = ClassFileReader.read(f);
assertNotNull( myClassReader );
IBinaryField[] fields = myClassReader.getFields();
assertNotNull( fields );
Map<String,IBinaryField> map = new HashMap<String,IBinaryField>();
for ( IBinaryField field : fields )
{
String name = new String(field.getName());
map.put( name, field );
}
IBinaryField privateInt = map.get("privateInt");
assertNotNull( privateInt );
}
}
==========================
1) From the eclipse file menu, choose "Import
"Aaron Digulla" <digulla@hepe.com> wrote in message
news:2212aaece25928b1a4fe845b198f637b$1@www.eclipse.org...
> Andy Carroll wrote:
>
>> One other detail - if you decide that using reflection on an
>> uninitialized class does seem the correct mechanism for you to use, you
>> can get around the limitation that by default reflection only provides
>> access to the public aspects of a class, by installing a security policy
>> that grants your the code java.lang.reflect.ReflectPermission
>> suppressAllChecks permission.
>
> Thanks for the suggestion. I'm fixing a compiler that needs to load Java
> class files, so I have no intention whatsoever to tamper with security
> policies :-)
>
> But as you said, I'll need private class information (so I can throw
> errors like "foo is private"), so Reflection is a dead end. I remember
> darkly to have tried to example class files a few years ago and wondering
> why there is no "class file API" which just allows to examine it. I
> figured it would make writing class loaders and Reflection code much more
> simple if that existed.
>
> Regards,
> Aaron Digulla
>
|
|
|
Goto Forum:
Current Time: Mon May 12 08:37:41 EDT 2025
Powered by FUDForum. Page generated in 0.07606 seconds
|