Some classes decompiled (via custom `ClassFileDecompilers.Light`) produce `InvalidMirrorException: stub:[PsiMethod:Checker...`

Answered

Hello, I am developing jd-intellij, which is the IDEA integration of JD-Core, which is a java decompiler (you probably heard about JD-GUI).

I identified a case, likely not the only one where IntelliJ IDEA reports an error in the mirror, this does not happen on all classes though.

 

Here's the reported stacktrace with the `javax.annotations.RegEx` (findbugs jsr305 1.3.9 annotations)

jar:///Users/brice/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/1.3.9/40719ea6961c0cb6afaeb6a921eaa1f6afd4cfdf/jsr305-1.3.9.jar!/javax/annotation/RegEx.class

com.intellij.diagnostic.PluginException: stub:[PsiMethod:Checker, PsiMethod:forConstantValue]; mirror:[PsiMethod:forConstantValue] [Plugin: jd-intellij]
at com.intellij.psi.impl.compiled.ClsFileImpl.wrapException(ClsFileImpl.java:387)
at com.intellij.psi.impl.compiled.ClsFileImpl.getMirror(ClsFileImpl.java:355)
at com.intellij.psi.impl.compiled.ClsFileImpl.getDecompiledPsiFile(ClsFileImpl.java:396)
at com.intellij.codeInsight.folding.impl.FoldingUpdate.getFoldingsFor(FoldingUpdate.java:230)
at com.intellij.codeInsight.folding.impl.CodeFoldingManagerImpl.buildInitialFoldings(CodeFoldingManagerImpl.java:135)
at com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl.loadEditorInBackground(PsiAwareTextEditorImpl.java:48)
at com.intellij.openapi.fileEditor.impl.text.AsyncEditorLoader.lambda$scheduleLoading$0(AsyncEditorLoader.java:97)
at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:808)
at com.intellij.openapi.fileEditor.impl.text.AsyncEditorLoader.lambda$scheduleLoading$1(AsyncEditorLoader.java:95)
at com.intellij.openapi.application.impl.NonBlockingReadActionImpl$Submission.insideReadAction(NonBlockingReadActionImpl.java:521)
at com.intellij.openapi.application.impl.NonBlockingReadActionImpl$Submission.lambda$attemptComputation$3(NonBlockingReadActionImpl.java:468)
at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:1096)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.lambda$runInReadActionWithWriteActionPriority$0(ProgressIndicatorUtils.java:79)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runActionAndCancelBeforeWrite(ProgressIndicatorUtils.java:157)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.lambda$runWithWriteActionPriority$1(ProgressIndicatorUtils.java:119)
at com.intellij.openapi.progress.ProgressManager.lambda$runProcess$0(ProgressManager.java:57)
at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$2(CoreProgressManager.java:178)
at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:688)
at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:634)
at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:64)
at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:165)
at com.intellij.openapi.progress.ProgressManager.runProcess(ProgressManager.java:57)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runWithWriteActionPriority(ProgressIndicatorUtils.java:116)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runInReadActionWithWriteActionPriority(ProgressIndicatorUtils.java:79)
at com.intellij.openapi.application.impl.NonBlockingReadActionImpl$Submission.attemptComputation(NonBlockingReadActionImpl.java:486)
at com.intellij.openapi.application.impl.NonBlockingReadActionImpl$Submission.lambda$transferToBgThread$1(NonBlockingReadActionImpl.java:408)
at com.intellij.util.concurrency.BoundedTaskExecutor.doRun(BoundedTaskExecutor.java:216)
at com.intellij.util.concurrency.BoundedTaskExecutor.access$200(BoundedTaskExecutor.java:27)
at com.intellij.util.concurrency.BoundedTaskExecutor$1.execute(BoundedTaskExecutor.java:195)
at com.intellij.util.ConcurrencyUtil.runUnderThreadName(ConcurrencyUtil.java:213)
at com.intellij.util.concurrency.BoundedTaskExecutor$1.run(BoundedTaskExecutor.java:184)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:668)
at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:665)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1.run(Executors.java:665)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: com.intellij.psi.impl.compiled.ClsElementImpl$InvalidMirrorException: stub:[PsiMethod:Checker, PsiMethod:forConstantValue]; mirror:[PsiMethod:forConstantValue]
at com.intellij.psi.impl.compiled.ClsElementImpl.setMirrors(ClsElementImpl.java:274)
at com.intellij.psi.impl.compiled.ClsClassImpl.setMirror(ClsClassImpl.java:451)
at com.intellij.psi.impl.compiled.ClsElementImpl.setMirror(ClsElementImpl.java:256)
at com.intellij.psi.impl.compiled.ClsElementImpl.setMirrors(ClsElementImpl.java:277)
at com.intellij.psi.impl.compiled.ClsClassImpl.setMirror(ClsClassImpl.java:452)
at com.intellij.psi.impl.compiled.ClsElementImpl.setMirror(ClsElementImpl.java:256)
at com.intellij.psi.impl.compiled.ClsElementImpl.setMirrors(ClsElementImpl.java:277)
at com.intellij.psi.impl.compiled.ClsElementImpl.setMirrors(ClsElementImpl.java:269)
at com.intellij.psi.impl.compiled.ClsFileImpl.setFileMirror(ClsFileImpl.java:300)
at com.intellij.psi.impl.compiled.ClsFileImpl.lambda$getMirror$1(ClsFileImpl.java:350)
at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:688)
at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:634)
at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:64)
at com.intellij.openapi.progress.impl.CoreProgressManager.executeNonCancelableSection(CoreProgressManager.java:218)
at com.intellij.psi.impl.compiled.ClsFileImpl.getMirror(ClsFileImpl.java:349)
... 36 more

The code produced by the decompiled looks correct, and similar to the original. The stack traces seems to indicate an issue with the `Checker.forConstantValue` method.

/* Location: jar:///Users/brice/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/1.3.9/40719ea6961c0cb6afaeb6a921eaa1f6afd4cfdf/jsr305-1.3.9.jar!/javax/annotation/RegEx.class
* Java language version: 5
* Class File: 49.0
* JD-Core Version: 1.1.3
*/

package javax.annotation;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.annotation.meta.TypeQualifierNickname;
import javax.annotation.meta.TypeQualifierValidator;
import javax.annotation.meta.When;




@Documented
@Syntax("RegEx")
@Retention(RetentionPolicy.RUNTIME)
@TypeQualifierNickname
public @interface RegEx
{
When when() default When.ALWAYS;

public static class Checker
implements TypeQualifierValidator<RegEx>
{
public When forConstantValue(RegEx annotation, Object value) {
if (!(value instanceof String)) {
return When.NEVER;
}
try {
Pattern.compile((String)value);
} catch (PatternSyntaxException e) {
return When.NEVER;
}
return When.ALWAYS;
}
}
}

I have seen this invalid mirror error in many occasions. Is there something I can do ?

 

The source code is here : https://github.com/bric3/jd-intellij

 

 

 

 

 

 

5 comments
Comment actions Permalink

I have another similar issue when inspecting some local jar files, for example I was able to reproduce the following stack trace, by downloading the spring-jdbc jar and adding it as a local jar dependency, when inspecting NamedParameterJdbcOperations this raises a similar error about invalid mirror.

    implementation(files("lib/spring-jdbc-4.1.6.RELEASE.jar"))
jar:///Users/brice/opensource/sandbox/lib/spring-jdbc-4.1.6.RELEASE.jar!/org/springframework/jdbc/core/namedparam/NamedParameterJdbcOperations.class

com.intellij.diagnostic.PluginException: stub:{}; mirror:null [Plugin: jd-intellij]
	at com.intellij.psi.impl.compiled.ClsFileImpl.wrapException(ClsFileImpl.java:387)
	at com.intellij.psi.impl.compiled.ClsFileImpl.getMirror(ClsFileImpl.java:355)
	at com.intellij.psi.impl.compiled.ClsElementImpl.getMirror(ClsElementImpl.java:140)
	at com.intellij.psi.impl.compiled.ClsElementImpl.getTextOffset(ClsElementImpl.java:177)
	at com.intellij.ide.util.EditSourceUtil.getDescriptor(EditSourceUtil.java:41)
	at com.intellij.ide.util.PsiNavigationSupportImpl.getDescriptor(PsiNavigationSupportImpl.java:31)
	at com.intellij.psi.impl.PsiElementBase.navigate(PsiElementBase.java:194)
	at com.intellij.codeInsight.navigation.NavigationUtil.lambda$openFileWithPsiElement$2(NavigationUtil.java:178)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:216)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:172)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:162)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:148)
	at com.intellij.codeInsight.navigation.NavigationUtil.openFileWithPsiElement(NavigationUtil.java:171)
	at com.intellij.ide.projectView.impl.nodes.AbstractPsiBasedNode.navigate(AbstractPsiBasedNode.java:235)
	at com.intellij.ide.projectView.impl.nodes.AbstractPsiBasedNode.navigate(AbstractPsiBasedNode.java:245)
	at com.intellij.util.OpenSourceUtil.navigateToSource(OpenSourceUtil.java:119)
	at com.intellij.util.OpenSourceUtil.navigate(OpenSourceUtil.java:74)
	at com.intellij.util.OpenSourceUtil.navigate(OpenSourceUtil.java:58)
	at com.intellij.util.OpenSourceUtil.navigate(OpenSourceUtil.java:51)
	at com.intellij.util.OpenSourceUtil.openSourcesFrom(OpenSourceUtil.java:19)
	at com.intellij.util.EditSourceOnDoubleClickHandler$TreeMouseListener.processDoubleClick(EditSourceOnDoubleClickHandler.java:166)
	at com.intellij.util.EditSourceOnDoubleClickHandler$TreeMouseListener.onDoubleClick(EditSourceOnDoubleClickHandler.java:160)
	at com.intellij.ui.DoubleClickListener.onClick(DoubleClickListener.java:30)
	at com.intellij.ui.ClickListener$1.mouseReleased(ClickListener.java:59)
	at java.desktop/java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:298)
	at java.desktop/java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:297)
	at java.desktop/java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:297)
	at java.desktop/java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:297)
	at java.desktop/java.awt.Component.processMouseEvent(Component.java:6652)
	at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3345)
	at com.intellij.ui.treeStructure.Tree.processMouseEvent(Tree.java:392)
	at com.intellij.ide.dnd.aware.DnDAwareTree.processMouseEvent(DnDAwareTree.java:45)
	at java.desktop/java.awt.Component.processEvent(Component.java:6417)
	at java.desktop/java.awt.Container.processEvent(Container.java:2263)
	at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5027)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2321)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4859)
	at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4918)
	at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4547)
	at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4488)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2307)
	at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2784)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4859)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:778)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:727)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:751)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:749)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:748)
	at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:969)
	at com.intellij.ide.IdeEventQueue.dispatchMouseEvent(IdeEventQueue.java:906)
	at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:836)
	at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$8(IdeEventQueue.java:449)
	at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:808)
	at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$9(IdeEventQueue.java:448)
	at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:781)
	at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:502)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Caused by: com.intellij.psi.impl.compiled.ClsElementImpl$InvalidMirrorException: stub:{}; mirror:null
	at com.intellij.psi.impl.compiled.ClsElementImpl.setMirrorIfPresent(ClsElementImpl.java:261)
	at com.intellij.psi.impl.compiled.ClsMethodImpl.setMirror(ClsMethodImpl.java:212)
	at com.intellij.psi.impl.compiled.ClsElementImpl.setMirror(ClsElementImpl.java:256)
	at com.intellij.psi.impl.compiled.ClsElementImpl.setMirrors(ClsElementImpl.java:277)
	at com.intellij.psi.impl.compiled.ClsClassImpl.setMirror(ClsClassImpl.java:451)
	at com.intellij.psi.impl.compiled.ClsElementImpl.setMirror(ClsElementImpl.java:256)
	at com.intellij.psi.impl.compiled.ClsElementImpl.setMirrors(ClsElementImpl.java:277)
	at com.intellij.psi.impl.compiled.ClsElementImpl.setMirrors(ClsElementImpl.java:269)
	at com.intellij.psi.impl.compiled.ClsFileImpl.setFileMirror(ClsFileImpl.java:300)
	at com.intellij.psi.impl.compiled.ClsFileImpl.lambda$getMirror$1(ClsFileImpl.java:350)
	at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:688)
	at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:634)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:64)
	at com.intellij.openapi.progress.impl.CoreProgressManager.executeNonCancelableSection(CoreProgressManager.java:218)
	at com.intellij.psi.impl.compiled.ClsFileImpl.getMirror(ClsFileImpl.java:349)
	... 66 more
0
Comment actions Permalink

The class produced by the decompiler doesn't have exactly the same set of members as the stub for the same file; one may compare a decompiled class with ClsFileImpl#decompile output to find out the difference.

1
Comment actions Permalink

Yann Cebron Thanks for the feedback, I'll take a look.

I wonder if this might be caused by synthetic members like default constructor, for the RegEx case

stub:[PsiMethod:Checker, PsiMethod:forConstantValue]; mirror:[PsiMethod:forConstantValue]

 

But for the classes in spring-jdbc, I can't guess at this time.

Anyway I'll test and report back.

 

0
Comment actions Permalink

Yann Cebron Indeed I can confirm you that in the case of the RegEx class it's the default constructor.

 

My decompiler don't output the default (and empty) constructor

package javax.annotation;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.annotation.meta.TypeQualifierNickname;
import javax.annotation.meta.TypeQualifierValidator;
import javax.annotation.meta.When;




@Documented
@Syntax("RegEx")
@Retention(RetentionPolicy.RUNTIME)
@TypeQualifierNickname
public @interface RegEx
{
When when() default When.ALWAYS;

public static class Checker
implements TypeQualifierValidator<RegEx>
{
public When forConstantValue(RegEx annotation, Object value) {
if (!(value instanceof String)) {
return When.NEVER;
}
try {
Pattern.compile((String)value);
} catch (PatternSyntaxException e) {
return When.NEVER;
}
return When.ALWAYS;
}
}
}

 

ClsFileImpl.decompile however emit this constructor

package javax.annotation;

@java.lang.annotation.Documented
@javax.annotation.Syntax("RegEx")
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@javax.annotation.meta.TypeQualifierNickname
public @interface RegEx {
javax.annotation.meta.When when() default javax.annotation.meta.When.ALWAYS;

static class Checker implements javax.annotation.meta.TypeQualifierValidator<javax.annotation.RegEx> {
public Checker() { /* compiled code */ }

public javax.annotation.meta.When forConstantValue(javax.annotation.RegEx annotation, java.lang.Object value) { /* compiled code */ }
}
}

 

Actual source code

package javax.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.annotation.meta.TypeQualifierNickname;
import javax.annotation.meta.TypeQualifierValidator;
import javax.annotation.meta.When;

/**
* This qualifier is used to denote String values that should be a Regular
* expression.
*
*/
@Documented
@Syntax("RegEx")
@TypeQualifierNickname
@Retention(RetentionPolicy.RUNTIME)
public @interface RegEx {
When when() default When.ALWAYS;

static class Checker implements TypeQualifierValidator<RegEx> {

public When forConstantValue(RegEx annotation, Object value) {
if (!(value instanceof String))
return When.NEVER;

try {
Pattern.compile((String) value);
} catch (PatternSyntaxException e) {
return When.NEVER;
}
return When.ALWAYS;

}

}

}

 

I don't think I can do something about it, except making the decompiler less smart. Maybe I can open an issue on Youtrack ?

 

 

 

 

0
Comment actions Permalink

Hi Brice,

the current implementation requires the decompiler to provide the same class outline as the stub builder, and the latter does include default constructors - because to tell a synthesized default constructor from a "real" one, one has to analyze the bytecode, and that's not what the stub builder should do, for the sake of speed of indexing. Oh, and for "real" empty constructors, there's no way at all.

For FernFlower, we had to add an option to it to always generate a default constructor - maybe that's possible for JD?

0

Please sign in to leave a comment.