Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-9541

Compiling statically using GroovyClassLoader does not always respect the configured parent ClassLoader

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Closed
    • Major
    • Resolution: Fixed
    • 3.0.3
    • 3.0.14, 4.0.7
    • None
    • None

    Description

      When using GroovyClassLoader to parse a groovy script annotated with @CompileStatic, the static type checker does not seem to use the configured parent class loader. Instead it uses the thread context ClassLoader. This sometimes causes compilation failures (especially when Closures are present) in environments like OSGi, where the thread context ClassLoader is definitely not the desired ClassLoader.
      For example this code:

      try (GroovyClassLoader classLoader = new GroovyClassLoader(bundleClassLoader,
                      compilerConfiguration))
      {
          Class<?> scriptClass = classLoader.parseClass(scriptContent, scriptName);
      }
      

      can fail like this:

      java.lang.RuntimeException: java.lang.NoClassDefFoundError: groovy/lang/GroovyObject
              at org.codehaus.groovy.control.CompilationUnit$IPrimaryClassNodeOperation.doPhaseOperation(CompilationUnit.java:955)
              at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:650)
              at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:627)
              at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:389)
              at groovy.lang.GroovyClassLoader.lambda$parseClass$3(GroovyClassLoader.java:332)
              at org.codehaus.groovy.runtime.memoize.StampedCommonCache.compute(StampedCommonCache.java:163)
              at org.codehaus.groovy.runtime.memoize.StampedCommonCache.getAndPut(StampedCommonCache.java:154)
              at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:330)
              at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:314)
              at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:257)
              ....
      Caused by: java.lang.NoClassDefFoundError: groovy/lang/GroovyObject
              at java.lang.ClassLoader.defineClass1(Native Method)
              at java.lang.ClassLoader.defineClass(ClassLoader.java:757)
              at java.lang.ClassLoader.defineClass(ClassLoader.java:636)
              at groovy.lang.GroovyClassLoader.defineClass(GroovyClassLoader.java:733)
              at org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.evaluateExpression(StaticTypeCheckingSupport.java:2176)
              at org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.checkClosureWithDelegatesTo(StaticTypeCheckingVisitor.java:3101)
              at org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.visitMethodCallArguments(StaticTypeCheckingVisitor.java:2645)
              at org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.visitMethodCallExpression(StaticTypeCheckingVisitor.java:3482)
              at org.codehaus.groovy.transform.sc.StaticCompilationVisitor.visitMethodCallExpression(StaticCompilationVisitor.java:409)
              at org.codehaus.groovy.ast.expr.MethodCallExpression.visit(MethodCallExpression.java:76)
              at org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.visitBinaryExpression(StaticTypeCheckingVisitor.java:770)
              at org.codehaus.groovy.ast.CodeVisitorSupport.visitDeclarationExpression(CodeVisitorSupport.java:335)
              at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitDeclarationExpression(ClassCodeVisitorSupport.java:150)
              at org.codehaus.groovy.ast.expr.DeclarationExpression.visit(DeclarationExpression.java:89)
              at org.codehaus.groovy.ast.CodeVisitorSupport.visitExpressionStatement(CodeVisitorSupport.java:117)
              at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitExpressionStatement(ClassCodeVisitorSupport.java:200)
              at org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.visitExpressionStatement(StaticTypeCheckingVisitor.java:2120)
              at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:40)
              at org.codehaus.groovy.ast.CodeVisitorSupport.visitBlockStatement(CodeVisitorSupport.java:86)
              at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitBlockStatement(ClassCodeVisitorSupport.java:164)
              at org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.visitBlockStatement(StaticTypeCheckingVisitor.java:3825)
              at org.codehaus.groovy.ast.stmt.BlockStatement.visit(BlockStatement.java:69)
              at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClassCodeContainer(ClassCodeVisitorSupport.java:138)
              at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitConstructorOrMethod(ClassCodeVisitorSupport.java:111)
              at org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.visitConstructorOrMethod(StaticTypeCheckingVisitor.java:2109)
              at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitMethod(ClassCodeVisitorSupport.java:106)
              at org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.startMethodInference(StaticTypeCheckingVisitor.java:2477)
              at org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.visitMethod(StaticTypeCheckingVisitor.java:2435)
              at org.codehaus.groovy.transform.sc.StaticCompilationVisitor.visitMethod(StaticCompilationVisitor.java:235)
              at org.codehaus.groovy.ast.ClassNode.visitMethods(ClassNode.java:1100)
              at org.codehaus.groovy.ast.ClassNode.visitContents(ClassNode.java:1093)
              at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClass(ClassCodeVisitorSupport.java:52)
              at org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.visitClass(StaticTypeCheckingVisitor.java:405)
              at org.codehaus.groovy.transform.sc.StaticCompilationVisitor.visitClass(StaticCompilationVisitor.java:193)
              at org.codehaus.groovy.transform.sc.StaticCompileTransformation.visit(StaticCompileTransformation.java:65)
              at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
              at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
              at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
              at java.lang.reflect.Method.invoke(Method.java:498)
              at org.codehaus.groovy.runtime.callsite.PlainObjectMetaMethodSite.doInvoke(PlainObjectMetaMethodSite.java:43)
              at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSite.invoke(PojoMetaMethodSite.java:191)
              at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
              at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:148)
              at org.codehaus.groovy.control.customizers.ASTTransformationCustomizer.call(ASTTransformationCustomizer.groovy:297)
              at org.codehaus.groovy.control.CompilationUnit$IPrimaryClassNodeOperation.doPhaseOperation(CompilationUnit.java:921)
              ... 30 more
      Caused by: java.lang.ClassNotFoundException: groovy.lang.GroovyObject
              at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
              at java.lang.ClassLoader.loadClass(ClassLoader.java:419)
              at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:869)
              at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:979)
              at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:967)
              ... 75 more
      

      This can happen if the compilation has been triggered by a thread started by another OSGi bundle that does not import groovy packages, even though the GroovyClassLoader has been explicitly created with the correct parent ClassLoader that can indeed access those packages.

      So far I have tracked the issue to org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport#evaluateExpression(final Expression, final CompilerConfiguration)
      where it creates a CompilationUnit without giving it a ClassLoader, which results in the CompilationUnit creating a default GroovyClassLoader with parent set to the thread context ClassLoader. I think that StaticTypeCheckingSupport should be able to pass the parent ClassLoader that the original GroovyClassLoader was configured with in order to avoid unexpected results.

      I don't know whether there are more places during static type checking where the issue could occur.

      The current workaround for this issue is to temporarily change the thread's context ClassLoader to the one that is given to the GroovyClassLoader before compiling a script, and then restoring the original context ClassLoader. This is, however, probably not possible in all environments.

      Attachments

        Activity

          People

            emilles Eric Milles
            latanasov Lyuben Atanasov
            Votes:
            3 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: