Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a custom record constructor prevents successful compilation #10045

Open
andreaskornstaedt opened this issue Nov 14, 2024 · 3 comments
Open

Comments

@andreaskornstaedt
Copy link

andreaskornstaedt commented Nov 14, 2024

**GWT version: 2.12.1
**Browser (with version): any
**Operating System: any


Description

A custom record constructor prevents successful compilation even if never invoked

         Compiling 1 permutation
            Compiling permutation 0...
            [ERROR] An internal compiler exception occurred
com.google.gwt.dev.jjs.InternalCompilerException: Unexpected error during visit.
	at com.google.gwt.dev.jjs.ast.JVisitor.translateException(JVisitor.java:111)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:130)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
	at com.google.gwt.dev.jjs.ast.JTransformer.transform(JTransformer.java:1100)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transform(GenerateJavaScriptAST.java:2659)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformDeclarationStatement(GenerateJavaScriptAST.java:686)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformDeclarationStatement(GenerateJavaScriptAST.java:520)
	at com.google.gwt.dev.jjs.ast.JTransformer$JRewriterVisitor.visit(JTransformer.java:725)
	at com.google.gwt.dev.jjs.ast.JDeclarationStatement.traverse(JDeclarationStatement.java:46)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
	at com.google.gwt.dev.jjs.ast.JTransformer.transform(JTransformer.java:1100)
	at com.google.gwt.dev.jjs.ast.JTransformer.transformIntoExcludingNulls(JTransformer.java:1120)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformBlock(GenerateJavaScriptAST.java:583)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformBlock(GenerateJavaScriptAST.java:520)
	at com.google.gwt.dev.jjs.ast.JTransformer$JRewriterVisitor.visit(JTransformer.java:647)
	at com.google.gwt.dev.jjs.ast.JBlock.traverse(JBlock.java:93)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
	at com.google.gwt.dev.jjs.ast.JTransformer.transform(JTransformer.java:1100)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transform(GenerateJavaScriptAST.java:2672)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformMethodBody(GenerateJavaScriptAST.java:833)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformMethodBody(GenerateJavaScriptAST.java:520)
	at com.google.gwt.dev.jjs.ast.JTransformer$JRewriterVisitor.visit(JTransformer.java:854)
	at com.google.gwt.dev.jjs.ast.JMethodBody.traverse(JMethodBody.java:81)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
	at com.google.gwt.dev.jjs.ast.JTransformer.transform(JTransformer.java:1100)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformMethod(GenerateJavaScriptAST.java:804)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.generatePrototypeDefinitions(GenerateJavaScriptAST.java:2474)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.generateTypeSetup(GenerateJavaScriptAST.java:1951)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformDeclaredType(GenerateJavaScriptAST.java:649)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformDeclaredType(GenerateJavaScriptAST.java:520)
	at com.google.gwt.dev.jjs.ast.JTransformer.transformClassType(JTransformer.java:95)
	at com.google.gwt.dev.jjs.ast.JTransformer$JRewriterVisitor.visit(JTransformer.java:695)
	at com.google.gwt.dev.jjs.ast.JClassType.traverse(JClassType.java:145)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
	at com.google.gwt.dev.jjs.ast.JTransformer.transform(JTransformer.java:1100)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformProgram(GenerateJavaScriptAST.java:1242)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformProgram(GenerateJavaScriptAST.java:520)
	at com.google.gwt.dev.jjs.ast.JTransformer$JRewriterVisitor.visit(JTransformer.java:943)
	at com.google.gwt.dev.jjs.ast.JProgram.traverse(JProgram.java:1248)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
	at com.google.gwt.dev.jjs.ast.JTransformer.transform(JTransformer.java:1100)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST.execImpl(GenerateJavaScriptAST.java:3171)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST.exec(GenerateJavaScriptAST.java:2944)
	at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.compilePermutation(JavaToJavaScriptCompiler.java:380)
	at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.compilePermutation(JavaToJavaScriptCompiler.java:274)
	at com.google.gwt.dev.CompilePerms.compile(CompilePerms.java:198)
	at com.google.gwt.dev.ThreadedPermutationWorkerFactory$ThreadedPermutationWorker.compile(ThreadedPermutationWorkerFactory.java:50)
	at com.google.gwt.dev.PermutationWorkerFactory$Manager$WorkerThread.run(PermutationWorkerFactory.java:74)
	at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.NullPointerException: Cannot invoke "String.isEmpty()" because "actualJsName" is null
	at com.google.gwt.dev.jjs.ast.JMethod.getQualifiedJsName(JMethod.java:172)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformNewInstance(GenerateJavaScriptAST.java:1002)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformNewInstance(GenerateJavaScriptAST.java:520)
	at com.google.gwt.dev.jjs.ast.JTransformer$JRewriterVisitor.visit(JTransformer.java:884)
	at com.google.gwt.dev.jjs.ast.JNewInstance.traverse(JNewInstance.java:73)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
	... 52 more
               [ERROR] at Intro.java(36): new Rec("1")
                  com.google.gwt.dev.jjs.ast.JNewInstance
               [ERROR] at Intro.java(36): Rec r = new Rec("1")
                  com.google.gwt.dev.jjs.ast.JDeclarationStatement
               [ERROR] at Intro.java(33): {
  Rec r = new Rec("1");
  Window.alert(r.toString());
}
                  com.google.gwt.dev.jjs.ast.JBlock
               [ERROR] at Intro.java(33): {
  Rec r = new Rec("1");
  Window.alert(r.toString());
}
                  com.google.gwt.dev.jjs.ast.JMethodBody
               [ERROR] at Intro.java(22): co.intro.client.Intro (extends Object implements EntryPoint)
                  com.google.gwt.dev.jjs.ast.JClassType
               [ERROR] at Unknown(0): <JProgram>
                  com.google.gwt.dev.jjs.ast.JProgram
            [ERROR] Unrecoverable exception, shutting down
com.google.gwt.core.ext.UnableToCompleteException: (see previous log entries)
	at com.google.gwt.dev.javac.CompilationProblemReporter.logAndTranslateException(CompilationProblemReporter.java:106)
	at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.compilePermutation(JavaToJavaScriptCompiler.java:461)
	at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.compilePermutation(JavaToJavaScriptCompiler.java:274)
	at com.google.gwt.dev.CompilePerms.compile(CompilePerms.java:198)
	at com.google.gwt.dev.ThreadedPermutationWorkerFactory$ThreadedPermutationWorker.compile(ThreadedPermutationWorkerFactory.java:50)
	at com.google.gwt.dev.PermutationWorkerFactory$Manager$WorkerThread.run(PermutationWorkerFactory.java:74)
	at java.base/java.lang.Thread.run(Thread.java:1583)
            [ERROR] Not all permutation were compiled , completed (0/1)
      [WARN] recompile failed
      [WARN] continuing to serve previous version
Steps to reproduce
public record Rec(String s) {
	public Rec(int i) {
		this("" + i);
	}
}

invoked like this

public void onModuleLoad() {
	Rec r = new Rec("1");
	Window.alert(r.toString());
}
Known workarounds

Don't use custom constructors

Links to further discussions
@niloc132
Copy link
Member

niloc132 commented Nov 16, 2024

Workaround:
Declare the default constructor. In this example, that would look like:

public record Rec(String s) {
        public Rec { }
	public Rec(int i) {
		this("" + i);
	}
}

Two stack traces I've identified that can come from this bug - this looks like the one reported:

Caused by: java.lang.NullPointerException: Cannot invoke "String.isEmpty()" because "actualJsName" is null
	at com.google.gwt.dev.jjs.ast.JMethod.getQualifiedJsName(JMethod.java:172)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformNewInstance(GenerateJavaScriptAST.java:1002)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformNewInstance(GenerateJavaScriptAST.java:520)
	at com.google.gwt.dev.jjs.ast.JTransformer$JRewriterVisitor.visit(JTransformer.java:884)
	at com.google.gwt.dev.jjs.ast.JNewInstance.traverse(JNewInstance.java:73)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
	... 52 more

And another that is similar, but not quite the same:

Caused by: java.lang.NullPointerException: Cannot invoke "String.isEmpty()" because "actualJsName" is null
	at com.google.gwt.dev.jjs.ast.JMethod.getQualifiedJsName(JMethod.java:172)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.dispatchToSuper(GenerateJavaScriptAST.java:920)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformMethodCall(GenerateJavaScriptAST.java:898)
	at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformMethodCall(GenerateJavaScriptAST.java:520)
	at com.google.gwt.dev.jjs.ast.JTransformer$JRewriterVisitor.visit(JTransformer.java:860)
	at com.google.gwt.dev.jjs.ast.JMethodCall.traverse(JMethodCall.java:265)
	at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
	... 58 more

The bug is that when being called in this way, the "super" constructor is incorrectly identified as being a native method as it has no body. I'm still working out why it has no body - it appears that either JDT doesn't emit a body for the canonical record constructor if there is a non-default constructor declared, or GWT just doesn't read it properly - but if the canonical constructor is declared or no constructors are declared at all, it will correctly have the field assignments as expected.

@niloc132
Copy link
Member

This is an upstream JDT bug, already fixed there: eclipse-jdt/eclipse.jdt.core#365. It appears that updating to 3.33 may resolve this. That version is also compiled to work with Java 11 so we should be safe to update, but it might be risky to do so on a point release like 2.12.2.

We should be able to work around it in a way that still works when JDT is later updated, by detecting if the body is missing from the canonical constructor, and if so, generate the field assignments. There is a caveat here that this must end up in the GwtAstBuilder rather than in ImplementRecordComponents - since the field assignments never were generated, the first pruneDeadFieldsAndMethods pass that runs as part of UnifyAst.exec ends up removing those fields outright, so we can't generate the correct constructor later.

@andreaskornstaedt
Copy link
Author

Workaround: Declare the default constructor. In this example, that would look like:

public record Rec(String s) {
        public Rec { }
	public Rec(int i) {
		this("" + i);
	}
}

Very helpful 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants