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

java.lang.ClassCastException: class com.google.gwt.dev.jjs.ast.JPrimitiveType cannot be cast to class com.google.gwt.dev.jjs.ast.JArrayType #10086

Open
optyfr opened this issue Jan 14, 2025 · 10 comments

Comments

@optyfr
Copy link

optyfr commented Jan 14, 2025

GWT version:
2.12.0 + 2.12.1
Browser (with version):
any
Operating System:
windows 11 and Temurin Java 17

Description

[ERROR] Unexpected internal compiler error
java.lang.ClassCastException: class com.google.gwt.dev.jjs.ast.JPrimitiveType cannot be cast to class com.google.gwt.dev.jjs.ast.JArrayType (com.google.gwt.dev.jjs.ast.JPrimitiveType and com.google.gwt.dev.jjs.ast.JArrayType are in unnamed module of loader 'app')
at com.google.gwt.dev.jjs.impl.GwtAstBuilder$AstVisitor.visit(GwtAstBuilder.java:1215)
at org.eclipse.jdt.internal.compiler.ast.ReferenceExpression.traverse(ReferenceExpression.java:1111)
at org.eclipse.jdt.internal.compiler.ast.MessageSend.traverse(MessageSend.java:1173)
at org.eclipse.jdt.internal.compiler.ast.MessageSend.traverse(MessageSend.java:1173)
at org.eclipse.jdt.internal.compiler.ast.Block.traverse(Block.java:154)
at org.eclipse.jdt.internal.compiler.ast.IfStatement.traverse(IfStatement.java:331)
at org.eclipse.jdt.internal.compiler.ast.Block.traverse(Block.java:154)
at org.eclipse.jdt.internal.compiler.ast.IfStatement.traverse(IfStatement.java:333)
at org.eclipse.jdt.internal.compiler.ast.Block.traverse(Block.java:154)
at org.eclipse.jdt.internal.compiler.ast.LambdaExpression.traverse(LambdaExpression.java:795)
at org.eclipse.jdt.internal.compiler.ast.MessageSend.traverse(MessageSend.java:1173)
at org.eclipse.jdt.internal.compiler.ast.Block.traverse(Block.java:154)
at org.eclipse.jdt.internal.compiler.ast.Initializer.traverse(Initializer.java:159)
at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.traverse(TypeDeclaration.java:1758)
at org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression.traverse(QualifiedAllocationExpression.java:693)
at org.eclipse.jdt.internal.compiler.ast.MessageSend.traverse(MessageSend.java:1173)
at org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration.traverse(ConstructorDeclaration.java:708)
at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.traverse(TypeDeclaration.java:1699)
at com.google.gwt.dev.jjs.impl.GwtAstBuilder.processImpl(GwtAstBuilder.java:4122)
at com.google.gwt.dev.jjs.impl.GwtAstBuilder.process(GwtAstBuilder.java:4160)
at com.google.gwt.dev.javac.CompilationStateBuilder$CompileMoreLater$UnitProcessorImpl.process(CompilationStateBuilder.java:128)
at com.google.gwt.dev.javac.JdtCompiler$CompilerImpl.process(JdtCompiler.java:322)
at org.eclipse.jdt.internal.compiler.Compiler.processCompiledUnits(Compiler.java:575)
at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:475)
at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:426)
at com.google.gwt.dev.javac.JdtCompiler.doCompile(JdtCompiler.java:1021)
at com.google.gwt.dev.javac.CompilationStateBuilder$CompileMoreLater.compile(CompilationStateBuilder.java:322)
at com.google.gwt.dev.javac.CompilationStateBuilder.doBuildFrom(CompilationStateBuilder.java:532)
at com.google.gwt.dev.javac.CompilationStateBuilder.buildFrom(CompilationStateBuilder.java:464)
at com.google.gwt.dev.cfg.ModuleDef.getCompilationState(ModuleDef.java:426)
at com.google.gwt.dev.Precompile.validate(Precompile.java:145)
at com.google.gwt.dev.Compiler.compile(Compiler.java:184)
at com.google.gwt.dev.Compiler.compile(Compiler.java:143)
at com.google.gwt.dev.Compiler.compile(Compiler.java:132)
at com.google.gwt.dev.Compiler$1.run(Compiler.java:110)
at com.google.gwt.dev.CompileTaskRunner.doRun(CompileTaskRunner.java:55)
at com.google.gwt.dev.CompileTaskRunner.runWithAppropriateLogger(CompileTaskRunner.java:50)
at com.google.gwt.dev.Compiler.main(Compiler.java:113)

Steps to reproduce

Problem started with 2.12.0, no problem with 2.11.0
No idea on what could be wrong with 2.12.0, but there is many things like this in my code :

protected static native JsArray<JavaScriptObject> getJSAJSO(JavaScriptObject jso, String name) /*-{
        return jso[name];
}-*/;

May this be related?

Known workarounds

switch back 2.11.0

Links to further discussions
@niloc132
Copy link
Member

Thanks for the report - this appears to say that the compiler encountered something like String[]::new (such as in list.toArray(String[]::new), but instead of returning some kind of array type, the method reference seems to return a primitive. If we had #10080 landed we would have more context about where this happens in your code.

I've added some quick tests to the 2.12.0 tagged commit to verify that primitive arrays are handled as expected - though this isn't quite the exception that you're seeing. Instead, the JDT seems to be saying that Foo[]::new has a synthetic method (we'll call it createFooArray) that has no body (as we expect, the implementation of how array constructors behave should be "obvious"), but we're finding that the signature of that method specifies that instead of returning Foo[], it returns some primitive.

Would you be able to build with a snapshot of 2.12.0 but with that PR landed, so we can see more specifically what is happening there? I can make a custom build that should log better information in the stack trace about what the code looked like that was being processed at the time.

@optyfr
Copy link
Author

optyfr commented Jan 14, 2025

yes replacing with a snapshot to try should be doable

I've some things like this in my code
Stream.of(records).map(r -> r.getAttribute("handle")).toArray(String[]::new);
and also
cb.apply(Stream.of(dsResponse.getData()).map(PathInfo::new).toArray(PathInfo[]::new));

@niloc132
Copy link
Member

Thanks, I'll put together a build asap for you to try.

That pattern should be well supported - I am also using it all over, with no problems on GWT 2.12.1.

@niloc132
Copy link
Member

A snapshot build of GWT, based on 2.12.0, with the change from #10080 cherry-picked on it is now available at https://repo.vertispan.com/gwt-snapshot/ with version 2.12.2-10086-array-methodref-SNAPSHOT. Please give that a try, and see if we get some better logs about what is going on?

@optyfr
Copy link
Author

optyfr commented Jan 15, 2025

here is the exception with the snapshot build :

com.google.gwt.dev.jjs.InternalCompilerException: Error constructing Java AST
        at com.google.gwt.dev.jjs.impl.GwtAstBuilder.translateException(GwtAstBuilder.java:4221)
        at com.google.gwt.dev.jjs.impl.GwtAstBuilder$AstVisitor.visit(GwtAstBuilder.java:1236)
        at org.eclipse.jdt.internal.compiler.ast.ReferenceExpression.traverse(ReferenceExpression.java:1111)
        at org.eclipse.jdt.internal.compiler.ast.MessageSend.traverse(MessageSend.java:1173)
        at org.eclipse.jdt.internal.compiler.ast.MessageSend.traverse(MessageSend.java:1173)
        at org.eclipse.jdt.internal.compiler.ast.Block.traverse(Block.java:154)
        at org.eclipse.jdt.internal.compiler.ast.IfStatement.traverse(IfStatement.java:331)
        at org.eclipse.jdt.internal.compiler.ast.Block.traverse(Block.java:154)
        at org.eclipse.jdt.internal.compiler.ast.IfStatement.traverse(IfStatement.java:333)
        at org.eclipse.jdt.internal.compiler.ast.Block.traverse(Block.java:154)
        at org.eclipse.jdt.internal.compiler.ast.LambdaExpression.traverse(LambdaExpression.java:795)
        at org.eclipse.jdt.internal.compiler.ast.MessageSend.traverse(MessageSend.java:1173)
        at org.eclipse.jdt.internal.compiler.ast.Block.traverse(Block.java:154)
        at org.eclipse.jdt.internal.compiler.ast.Initializer.traverse(Initializer.java:159)
        at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.traverse(TypeDeclaration.java:1758)
        at org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression.traverse(QualifiedAllocationExpression.java:693)
        at org.eclipse.jdt.internal.compiler.ast.MessageSend.traverse(MessageSend.java:1173)
        at org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration.traverse(ConstructorDeclaration.java:708)
        at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.traverse(TypeDeclaration.java:1699)
        at com.google.gwt.dev.jjs.impl.GwtAstBuilder.processImpl(GwtAstBuilder.java:4157)
        at com.google.gwt.dev.jjs.impl.GwtAstBuilder.process(GwtAstBuilder.java:4195)
        at com.google.gwt.dev.javac.CompilationStateBuilder$CompileMoreLater$UnitProcessorImpl.process(CompilationStateBuilder.java:128)
        at com.google.gwt.dev.javac.JdtCompiler$CompilerImpl.process(JdtCompiler.java:322)
        at org.eclipse.jdt.internal.compiler.Compiler.processCompiledUnits(Compiler.java:575)
        at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:475)
        at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:426)
        at com.google.gwt.dev.javac.JdtCompiler.doCompile(JdtCompiler.java:1021)
        at com.google.gwt.dev.javac.CompilationStateBuilder$CompileMoreLater.compile(CompilationStateBuilder.java:322)
        at com.google.gwt.dev.javac.CompilationStateBuilder.doBuildFrom(CompilationStateBuilder.java:532)
        at com.google.gwt.dev.javac.CompilationStateBuilder.buildFrom(CompilationStateBuilder.java:464)
        at com.google.gwt.dev.cfg.ModuleDef.getCompilationState(ModuleDef.java:426)
        at com.google.gwt.dev.Precompile.precompile(Precompile.java:210)
        at com.google.gwt.dev.Precompile.precompile(Precompile.java:190)
        at com.google.gwt.dev.Precompile.precompile(Precompile.java:131)
        at com.google.gwt.dev.Compiler.compile(Compiler.java:192)
        at com.google.gwt.dev.Compiler.compile(Compiler.java:143)
        at com.google.gwt.dev.Compiler.compile(Compiler.java:132)
        at com.google.gwt.dev.Compiler$1.run(Compiler.java:110)
        at com.google.gwt.dev.CompileTaskRunner.doRun(CompileTaskRunner.java:55)
        at com.google.gwt.dev.CompileTaskRunner.runWithAppropriateLogger(CompileTaskRunner.java:50)
        at com.google.gwt.dev.Compiler.main(Compiler.java:113)
Caused by: java.lang.ClassCastException: class com.google.gwt.dev.jjs.ast.JPrimitiveType cannot be cast to class com.google.gwt.dev.jjs.ast.JArrayType (com.google.gwt.dev.jjs.ast.JPrimitiveType and com.google.gwt.dev.jjs.ast.JArrayType are in unnamed module of loader 'app')
        at com.google.gwt.dev.jjs.impl.GwtAstBuilder$AstVisitor.visit(GwtAstBuilder.java:1220)
        ... 39 more
   [ERROR] at RemoteFileChooser.java(882): PathInfo[]::new
      org.eclipse.jdt.internal.compiler.ast.ReferenceExpression

and the winner is that line :
processPaths(context, cb, Stream.of(records).map(PathInfo::new).toArray(PathInfo[]::new));
with
processPaths defined as private void processPaths(String context, CallBack cb, PathInfo[] paths)
records as ListGridRecord[] records = list.getSelectedRecords(); (directly from smartGwt 13.0)
and PathInfo an inner class defined like this

	public class PathInfo
	{
		public String path;
		public String parent;
		public String name;

		public PathInfo(String path, String parent, String name)
		{
			this.path = path;
			this.parent = parent;
			this.name = name;
		}

		public PathInfo(Record record)
		{
			this(record.getAttribute("RelPath"), RemoteFileChooser.this.parent, record.getAttribute("Name"));
		}
	}

@optyfr
Copy link
Author

optyfr commented Jan 15, 2025

of course using processPaths(context, cb, Stream.of(records).map(PathInfo::new).collect(Collectors.toList()).toArray(new PathInfo[0])); is working... but that's not desirable

@niloc132
Copy link
Member

Thanks, I don't yet understand the issue, but I can reproduce it. Modifying the HelloWorld sample to this causes the bug to happen:

public class Hello implements EntryPoint {

  @Override
  @SuppressWarnings("ReturnValueIgnored")
  public void onModuleLoad() {
    Stream.of("hello").map(PathInfo::new).toArray(PathInfo[]::new);
  }

  public class PathInfo {
    public PathInfo(String value) {
    }
  }
}

My first thought is that there is a name collision of some kind between the two method references, but replacing the line in the entrypoint with

DoubleStream.of(1.0).mapToObj(Double::new).toArray(Double[]::new);

doesn't cause the issue.


of course using processPaths(context, cb, Stream.of(records).map(PathInfo::new).collect(Collectors.toList()).toArray(new PathInfo[0])); is working... but that's not desirable

As a developer, writing code, yes, I agree. As a developer reviewing the size of your application... lambdas generate significantly more code than the two extra method call. Almost certainly not enough for you to care about it, but it is worth keeping in mind.

@optyfr
Copy link
Author

optyfr commented Jan 15, 2025

well... if I had to search for particularities of PathInfo compared to Double, I would say that :

  • it's a nested class
  • it's more than a nested class it's an inner class (non static),
  • and it's not a final one (but it could be)

Then maybe the fact it's an inner class has something to do with this ?

@niloc132
Copy link
Member

It looks like it might be specific to being a non-static inner class - adding the static keyword to the class may also function as a workaround in some cases. Granted, you would need to pass in parent (and thus stop using the method reference for the constructor itself), but it does work around the issue.

For some reason, the array constructor factory method is marked as belonging to the current method's enclosing type (e.g. Hello in the sample above) if the type is static (or top-level), but if non-static, there are a few oddities that arise, according to JDT (not yet in GWT itself):

  • the method belongs to java.lang.Object
  • the method is a constructor for Object itself, though one that takes an integer param
  • the method has no body - if assertions are enabled for the JVM while compiling, a slightly earlier error occurs than what you are seeing. This assertion fails as java.lang.Object is an "external" type during this early phase of compilation, so all methods must already have bodies.

So, there are at least two things about the AST that JDT is giving us that need to be worked out here - presumably this is a change in JDT, possibly a bug that a newer version will resolve for us. I'll test an older release, and also see if #10082 might resolve this.

@optyfr
Copy link
Author

optyfr commented Jan 15, 2025

Ok, thank you
While you were writing here, I just transformed the class to a static one and it succeeded while I was reading your last comment :-)

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