mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 22:51:17 +07:00
[groovy] properly reset classloader caches after stub generation is finished (IDEA-322782)
If class loaders are used to resolve references to classes in groovyc, it may happen that class file for groovy file is compiled from generated stub after groovy stub generation finished. To avoid "Unable to load class" error, we need to clear caches of the class loader which was created before stub generation started. Also, we need to disable the usage of classpath.index files which are updated only when the module chunk is fully compiled. Second compilation is still needed in such cases, but given that it's performed automatically, it doesn't affect users much. GitOrigin-RevId: 53b265c8966df06900e6b05558ce01ea8dfa77e9
This commit is contained in:
committed by
intellij-monorepo-bot
parent
b906475501
commit
c2ff557fc2
@@ -108,7 +108,7 @@ final class InProcessGroovyc implements GroovycFlavor {
|
||||
//noinspection unchecked
|
||||
Queue<String> toGroovyc = (Queue<String>)msg;
|
||||
loader.resetCache();
|
||||
return createContinuation(future, toGroovyc, parser);
|
||||
return createContinuation(future, toGroovyc, parser, loader);
|
||||
}
|
||||
else if (msg != null) {
|
||||
throw new AssertionError("Unknown message: " + msg);
|
||||
@@ -119,11 +119,13 @@ final class InProcessGroovyc implements GroovycFlavor {
|
||||
@NotNull
|
||||
private static GroovycContinuation createContinuation(Future<Void> future,
|
||||
@NotNull Queue<String> mailbox,
|
||||
GroovycOutputParser parser) {
|
||||
GroovycOutputParser parser,
|
||||
@NotNull JointCompilationClassLoader loader) {
|
||||
return new GroovycContinuation() {
|
||||
@NotNull
|
||||
@Override
|
||||
public GroovyCompilerResult continueCompilation() throws Exception {
|
||||
loader.resetCache();
|
||||
parser.onContinuation();
|
||||
mailbox.offer(GroovyRtConstants.JAVAC_COMPLETED);
|
||||
future.get();
|
||||
@@ -206,6 +208,10 @@ final class InProcessGroovyc implements GroovycFlavor {
|
||||
return UrlClassLoader.build().
|
||||
files(toPaths(compilationClassPath))
|
||||
.parent(parent)
|
||||
/* obsolete classpath.index files are deleted only after compilation of the module chunk finishes, so they may not include *.class
|
||||
files produced by javac during compilation of this chunk;
|
||||
therefore, persistent index should be disabled for Groovy class loader */
|
||||
.usePersistentClasspathIndexForLocalClassDirectories(false)
|
||||
.useCache(ourLoaderCachePool, file -> {
|
||||
String filePath = FileUtil.toCanonicalPath(file.toString());
|
||||
for (String output : myOutputs) {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.groovy.compiler
|
||||
|
||||
import com.intellij.compiler.CompilerConfiguration
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.ReadAction
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import groovy.transform.CompileStatic
|
||||
import org.jetbrains.groovy.compiler.rt.GroovyRtConstants
|
||||
import org.jetbrains.jps.incremental.groovy.JpsGroovycRunner
|
||||
import org.jetbrains.plugins.groovy.TestLibrary
|
||||
|
||||
import static com.intellij.testFramework.EdtTestUtil.runInEdtAndWait
|
||||
@@ -56,6 +59,42 @@ class Bar {}'''
|
||||
assert msg.message.contains('org.apache.commons.logging.Log')
|
||||
}
|
||||
|
||||
void "test circular dependency with in-process class loading resolving"() {
|
||||
def groovyFile = myFixture.addFileToProject('mix/GroovyClass.groovy', '''
|
||||
package mix
|
||||
@groovy.transform.CompileStatic
|
||||
class GroovyClass {
|
||||
JavaClass javaClass
|
||||
String bar() {
|
||||
return javaClass.foo()
|
||||
}
|
||||
}
|
||||
''')
|
||||
myFixture.addFileToProject('mix/JavaClass.java', '''
|
||||
package mix;
|
||||
public class JavaClass {
|
||||
GroovyClass groovyClass;
|
||||
public String foo() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
''')
|
||||
CompilerConfiguration.getInstance(project).buildProcessVMOptions +=
|
||||
" -D$JpsGroovycRunner.GROOVYC_IN_PROCESS=true -D$GroovyRtConstants.GROOVYC_ASM_RESOLVING_ONLY=false"
|
||||
assertEmpty(make())
|
||||
|
||||
touch(groovyFile.virtualFile)
|
||||
|
||||
def messages = make()
|
||||
|
||||
/* since only groovy file is changed, its class file is deleted, but javac isn't called (JavaBuilder.compile returns early), so
|
||||
GroovyClass.class file from the generated stub isn't produced, and the classloader failed to load JavaClass during compilation of
|
||||
GroovyClass. After chunk rebuild is requested, javac is called so it compiles the stub and groovyc finishes successfully.
|
||||
*/
|
||||
assert messages.collect { it.message } == chunkRebuildMessage("Groovy compiler")
|
||||
}
|
||||
|
||||
|
||||
protected List<String> chunkRebuildMessage(String builder) {
|
||||
return ['Builder "' + builder + '" requested rebuild of module chunk "mainModule"']
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user