Issue Details (XML | Word | Printable)

Key: GROOVY-1863
Type: Bug Bug
Status: Closed Closed
Resolution: Fixed
Priority: Blocker Blocker
Assignee: Jochen Theodorou
Reporter: Aaron Digulla
Votes: 0
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
groovy

Groovy compiler executes code it is compiling

Created: 30/Apr/07 03:37 AM   Updated: 14/Oct/07 01:30 PM
Component/s: command line processing
Affects Version/s: 1.0
Fix Version/s: 1.1-rc-1

Time Tracking:
Not Specified

File Attachments: 1. File ClassExaminer.groovy (3 kB)
2. File patch (6 kB)
3. HTML File StaticInitBug.zip (0.6 kB)

Issue Links:
dependent
 


 Description  « Hide
Unpack the attached example and compile it. The compiler will crash with a RuntimeException like this:

{



 All   Comments   Work Log   Change History      Sort Order: Ascending order - Click to sort in descending order
Aaron Digulla added a comment - 30/Apr/07 03:43 AM
Unpack the attached example and compile it. The compiler will crash with a RuntimeException like this:
java.lang.ExceptionInInitializerError
	at sun.misc.Unsafe.ensureClassInitialized(Native Method)
	at sun.reflect.UnsafeFieldAccessorFactory.newFieldAccessor(UnsafeFieldAccessorFactory.java:25)
	at sun.reflect.ReflectionFactory.newFieldAccessor(ReflectionFactory.java:122)
	at java.lang.reflect.Field.acquireFieldAccessor(Field.java:918)
	at java.lang.reflect.Field.getFieldAccessor(Field.java:899)
	at java.lang.reflect.Field.get(Field.java:358)
	at org.codehaus.groovy.control.ResolveVisitor.getTimeStamp(ResolveVisitor.java:275)
	at org.codehaus.groovy.control.ResolveVisitor.isSourceNewer(ResolveVisitor.java:298)
	at org.codehaus.groovy.control.ResolveVisitor.resolveToScript(ResolveVisitor.java:327)
	at org.codehaus.groovy.control.ResolveVisitor.resolve(ResolveVisitor.java:248)
	at org.codehaus.groovy.control.ResolveVisitor.resolve(ResolveVisitor.java:226)
	at org.codehaus.groovy.control.ResolveVisitor.resolveOrFail(ResolveVisitor.java:211)
	at org.codehaus.groovy.control.ResolveVisitor.resolveOrFail(ResolveVisitor.java:221)
	at org.codehaus.groovy.control.ResolveVisitor.transformVariableExpression(ResolveVisitor.java:675)
	at org.codehaus.groovy.control.ResolveVisitor.transform(ResolveVisitor.java:538)
	at org.codehaus.groovy.control.ResolveVisitor.transformDeclarationExpression(ResolveVisitor.java:741)
	at org.codehaus.groovy.control.ResolveVisitor.transform(ResolveVisitor.java:542)
	at org.codehaus.groovy.control.ResolveVisitor.visitExpressionStatement(ResolveVisitor.java:839)
	at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:70)
	at org.codehaus.groovy.ast.CodeVisitorSupport.visitBlockStatement(CodeVisitorSupport.java:83)
	at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitBlockStatement(ClassCodeVisitorSupport.java:102)
	at org.codehaus.groovy.control.ResolveVisitor.visitBlockStatement(ResolveVisitor.java:845)
	at org.codehaus.groovy.ast.stmt.BlockStatement.visit(BlockStatement.java:82)
	at org.codehaus.groovy.control.ResolveVisitor.visitMethod(ResolveVisitor.java:181)
	at org.codehaus.groovy.ast.ClassNode.visitContents(ClassNode.java:838)
	at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClass(ClassCodeVisitorSupport.java:36)
	at org.codehaus.groovy.control.ResolveVisitor.visitClass(ResolveVisitor.java:787)
	at org.codehaus.groovy.control.ResolveVisitor.startResolving(ResolveVisitor.java:128)
	at org.codehaus.groovy.control.CompilationUnit$5.call(CompilationUnit.java:595)
	at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:833)
	at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:480)
	at org.codehaus.groovy.eclipse.core.compiler.GroovyCompiler.compile(GroovyCompiler.java:158)
	at org.codehaus.groovy.eclipse.core.compiler.GroovyCompiler.compile(GroovyCompiler.java:89)
	at org.codehaus.groovy.eclipse.core.model.GroovyProject.compileGroovyFile(GroovyProject.java:496)
	at org.codehaus.groovy.eclipse.core.GroovyCore.sourceChanged(GroovyCore.java:106)
	at org.codehaus.groovy.eclipse.editor.GroovyReconcilingStrategy.reconcile(GroovyReconcilingStrategy.java:78)
	at org.eclipse.jface.text.reconciler.MonoReconciler.process(MonoReconciler.java:71)
	at org.eclipse.jface.text.reconciler.AbstractReconciler$BackgroundThread.run(AbstractReconciler.java:204)
Caused by: java.lang.RuntimeException: This must not be executed during compile!
	at Foo.init(Foo.java:13)
	at Foo.<init>(Foo.java:8)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
	at org.codehaus.groovy.runtime.MetaClassHelper.doConstructorInvoke(MetaClassHelper.java:562)
	at groovy.lang.MetaClassImpl.doConstructorInvoke(MetaClassImpl.java:1756)
	at groovy.lang.MetaClassImpl.invokeConstructor(MetaClassImpl.java:758)
	at groovy.lang.MetaClassImpl.invokeConstructor(MetaClassImpl.java:688)
	at org.codehaus.groovy.runtime.Invoker.invokeConstructorOf(Invoker.java:163)
	at org.codehaus.groovy.runtime.InvokerHelper.invokeConstructorOf(InvokerHelper.java:140)
	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeNewN(ScriptBytecodeAdapter.java:243)
	at StaticInitBug.<clinit>(StaticInitBug.groovy:4)
	... 38 more

The problem is this: Foo does some strange stuff in its constructor.

StaticInitBug has a static initializer of type Foo.

Bar uses StaticInitBug. While the compiler compiles Bar, it tries to find out the lastModificationTime of the class StaticInitBug. This code resolves the class using reflection. The net result is that StaticInitBug is loaded and initialized which leads to the execution of the code in Foo.

This should not happen. I guess using reflection in the Groovy compiler is a "must not". Some other means to analyze a class without actually executing any code in it must be found.


Aaron Digulla added a comment - 03/May/07 07:25 AM
A small example how to read the lastModified date from a groovy class file without Reflection.

Aaron Digulla added a comment - 03/May/07 10:37 AM
Attached the original testcase for the bug again.

Robert Stroud added a comment - 08/May/07 03:17 AM
See discussion on groovy-dev mailing list - the difficulty is that the compiler uses Field.get to access a timestamp that was added to the compiled class. However, this causes the class to be initialized, which in turn causes static initializers to be executed.

http://www.nabble.com/Groovy-runs-code-in-static-initializers-during-compile-tf3607689.html

I don't think there's anything wrong with using Class objects to access type information about classes, so I don't think it's necessary to find an alternative way of reading a class file - however, care must be taken not to initialize the class file during compilation.

An alternative approach to solving this problem is to check for the existence of a source file and object file, and compare the file modification times - this avoids the need to store a modification time in the class file.

I've attached a patch that uses this approach - it runs the attached example without triggering the runtime exception from the compiler:

http://www.nabble.com/Groovy-runs-code-in-static-initializers-during-compile-tf3607689.html

This is just a "proof of concept", but I think it suggests another way of tackling this issue.


Aaron Digulla added a comment - 10/Oct/07 08:17 AM
In which revision has it been fixed?

Guillaume Laforge added a comment - 10/Oct/07 08:19 AM
in trunk

Aaron Digulla added a comment - 10/Oct/07 08:26 AM
In which revision in trunk?

I'd like to review the fix.



Aaron Digulla added a comment - 14/Oct/07 01:30 PM
The fix looks good to me, thanks!