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

 

 

 

 

 

 

12 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?

1
Comment actions Permalink

Hi Roman Shevchenko

Ah thanks for the feedback.

It was possible to remove default constructors in an earlier version, but now it has been removed after a huge refactoring, and it bring the decompiled source closer to the original source code. For now I'll have to live with that, unless there are other implications besides these error reports. I'll see how/if this can be re-added in jd-core.

that's not what the stub builder should do

Isn't synthetic "merely" a modifier. That would solve some of these issues I think.

 

 

 

 

 

0
Comment actions Permalink

`ACC_SYNTHETIC` is a modifier, but Javac doesn't set it for default constructors, unfortunately.

1
Comment actions Permalink

Ah yes of course, I forgot this quirk.

But surely there's a way, default constructor's bytecodes are usually very simple. They are no-arg (except `this`) and they "only" invoke the superclass constructor (`<init>`). The stub decompiler could only verify the bytecodes if the number of args is `1` (this) and if the stack size is size is `1`, if not then it's unlikely to be default constructor.

 

Skipping some javap output (the constant pool in particular, as javap resolves reference in comments).

public class A {
}

 stack=1, locals=1, args_size=1
0: aload_0 // this
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return

class B {
public B() {}
}

 stack=1, locals=1, args_size=1
0: aload_0 // this
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return

class C extends A {
}

  stack=1, locals=1, args_size=1
0: aload_0 // this
1: invokespecial #1 // Method A."<init>":()V
4: return

class D extends A {
public D() {}
}

 stack=1, locals=1, args_size=1
0: aload_0 // this
1: invokespecial #1 // Method A."<init>":()V
4: return

class E extends A {
public E() {
super();
}
}

 stack=1, locals=1, args_size=1
0: aload_0 // this
1: invokespecial #1 // Method A."<init>":()V
4: return

class F extends B {
}

stack=1, locals=1, args_size=1
0: aload_0 // this
1: invokespecial #1 // Method B."<init>":()V
4: return

 

 

0
Comment actions Permalink

It is definitely possible, but would significantly complicate the code (*) and slow indexing down to a certain degree - all without providing clear benefits. Even then, it won't be 100% correct - the bytecode of a generated constructor and a user-written empty one is identical.

(*) by the time `MethodVisitor::visitCode` is called, the decision on whether to skip the method has long been made

1
Comment actions Permalink

OK I cannot guess such a change implies on the code base and the performance impact. I understand the constraint and your point of view on the matter.

About the benefit, from my point of view it would allow to have a decompiled source closer to the original (default constructor are almost never written explicitly). But what I actually try to solve are the repetitive InvalidMirrorException errors. So that's the benif I'm really looking for.

Since they are reported, I don't quite measure the impact within the IDE of this invalid mirror. Maybe it's just reporting differences with the stub decompiler, maybe it has other implications.

 

Imagining things, could it make sense that on InvalidMirrorException the IDE calls back the decompiler plugin so that it has a chance to act on the invalid mirror like back feeding the missing constructor programmatically ? While maintaining the decompiled text in the editor.

 

 

By the way thank you Yann Cebron and Roman Shevchenko for spending time on this exchange ! It's really appreciated !

0
Comment actions Permalink

I understand your position, but being on both sides (I occasionally maintain the bundled decompiler), this problem is much easier to address on the decompiler side. Routing InvalidMirrorException back into the decompiler is probably possible, but it would make both the API and implementations too cumbersome.

The only real opportunity on the IDE side I can think of is making the stub/decompiled text pairing tolerant to missing default constructor.

0
Comment actions Permalink

Roman Shevchenko

Hi, sorry for the late reply, OK I understand your position as well, indeed thinking again on this certainly makes the API convoluted.

The only real opportunity on the IDE side I can think of is making the stub/decompiled text pairing tolerant to missing default constructor.

That would be awesome.

Thank you again for taking the time to exchange on this topic it is very appreciated !

 

 

0

Please sign in to leave a comment.