groovy

Groovy compiler executes code it is compiling

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Blocker Blocker
  • Resolution: Fixed
  • Affects Version/s: 1.0
  • Fix Version/s: 1.1-rc-1
  • Labels:
    None
  • Number of attachments :
    3

Description

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


{

Issue Links

Activity

Hide
Aaron Digulla added a comment -

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.

Show
Aaron Digulla added a comment - 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.
Hide
Aaron Digulla added a comment -

A small example how to read the lastModified date from a groovy class file without Reflection.

Show
Aaron Digulla added a comment - A small example how to read the lastModified date from a groovy class file without Reflection.
Hide
Aaron Digulla added a comment -

Attached the original testcase for the bug again.

Show
Aaron Digulla added a comment - Attached the original testcase for the bug again.
Hide
Robert Stroud added a comment -

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.

Show
Robert Stroud added a comment - 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.
Hide
Aaron Digulla added a comment -

In which revision has it been fixed?

Show
Aaron Digulla added a comment - In which revision has it been fixed?
Hide
Guillaume Laforge added a comment -

in trunk

Show
Guillaume Laforge added a comment - in trunk
Hide
Aaron Digulla added a comment -

In which revision in trunk?

I'd like to review the fix.

Show
Aaron Digulla added a comment - In which revision in trunk? I'd like to review the fix.
Hide
Aaron Digulla added a comment -

The fix looks good to me, thanks!

Show
Aaron Digulla added a comment - The fix looks good to me, thanks!

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: