gwtproject / gwt

GWT Open Source Project

Home Page:http://www.gwtproject.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

GWTTestCase & DevMode unable to handle expression in the form: "some string" + (someCondition ? "aValue" : aNonStringValue)

rdeangelis83 opened this issue · comments

helloworld-gwt.zip

GWT version: 2.9.0
Browser (with version): All
Operating System: All


Description

Code in GWTTestCases that contains expression in the form "some string" + (someCondition ? "aValue" : aNonStringValue) can not be compiled in DevMode.

As result an assertion in com.google.gwt.dev.javac.JdtUtil#signature() will be fired.

Steps to reproduce

See attached helloworld-gwt.zip. Or simply add following expression into your code GWTTestCase code and run in in devMode: "some string" + (someCondition ? "aValue" : aNonStringValue)

Known workarounds

Disable assertions solves the problem.
E.g. using the net.ltgt.gwt.maven:gwt-maven-plugin

<enableAssertions>false</enableAssertions>
Links to further discussions

“Classic” DevMode has been deprecated for nearly 4 years now (http://www.gwtproject.org/release-notes.html#Release_Notes_2_8_0_RC1; a bit less if you only account for non-RC versions, but still) so as long as it works in webMode I don't think this is something that's going to ever be fixed or even investigated.

As far what I understand is that it seems to be an bug in the eclipse compiler. It should also happen in non devMode but because the assertions are disabled it doesn't appear. But I'm not 100% sure.

We experience in superdev mode, I guess we must enable assertions by default ;)

Our workaround was just to eliminate the intersection type by always using/converting to strings.

We experience in superdev mode, I guess we must enable assertions by default ;)

Our workaround was just to eliminate the intersection type by always using/converting to strings.

Yeah it really seems that the assertions are disabled per default in superdev mode. Because the super dev mode works in my small attached example.

Full stack trace emitted by the compiler in that project:

[ERROR] Compiler aborted with an exception 
com.google.gwt.dev.jjs.InternalCompilerException: Error constructing Java AST
	at com.google.gwt.dev.jjs.impl.GwtAstBuilder.translateException(GwtAstBuilder.java:4033)
	at com.google.gwt.dev.jjs.impl.GwtAstBuilder$AstVisitor.endVisit(GwtAstBuilder.java:647)
	at org.eclipse.jdt.internal.compiler.ast.ConditionalExpression.traverse(ConditionalExpression.java:827)
	at org.eclipse.jdt.internal.compiler.ast.BinaryExpression.traverse(BinaryExpression.java:1920)
	at org.eclipse.jdt.internal.compiler.ast.BinaryExpression.traverse(BinaryExpression.java:1919)
	at org.eclipse.jdt.internal.compiler.ast.BinaryExpression.traverse(BinaryExpression.java:1919)
	at org.eclipse.jdt.internal.compiler.ast.CombinedBinaryExpression.traverse(CombinedBinaryExpression.java:370)
	at org.eclipse.jdt.internal.compiler.ast.ReturnStatement.traverse(ReturnStatement.java:402)
	at org.eclipse.jdt.internal.compiler.ast.MethodDeclaration.traverse(MethodDeclaration.java:365)
	at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.traverse(TypeDeclaration.java:1448)
	at com.google.gwt.dev.jjs.impl.GwtAstBuilder.processImpl(GwtAstBuilder.java:3969)
	at com.google.gwt.dev.jjs.impl.GwtAstBuilder.process(GwtAstBuilder.java:4007)
	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:1020)
	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:423)
	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.junit.JUnitShell.compileForWebMode(JUnitShell.java:1093)
	at com.google.gwt.junit.JUnitShell.maybeCompileForWebMode(JUnitShell.java:1158)
	at com.google.gwt.junit.CompileStrategy.maybeCompileModuleImpl2(CompileStrategy.java:183)
	at com.google.gwt.junit.CompileStrategy.maybeCompileModuleImpl(CompileStrategy.java:113)
	at com.google.gwt.junit.SimpleCompileStrategy.maybeCompileModule(SimpleCompileStrategy.java:36)
	at com.google.gwt.junit.JUnitShell.runTestImpl(JUnitShell.java:1349)
	at com.google.gwt.junit.JUnitShell.runTestImpl(JUnitShell.java:1317)
	at com.google.gwt.junit.JUnitShell.runTest(JUnitShell.java:681)
	at com.google.gwt.junit.client.GWTTestCase.runTest(GWTTestCase.java:421)
	at junit.framework.TestCase.runBare(TestCase.java:141)
	at junit.framework.TestResult$1.protect(TestResult.java:122)
	at junit.framework.TestResult.runProtected(TestResult.java:142)
	at junit.framework.TestResult.run(TestResult.java:125)
	at junit.framework.TestCase.run(TestCase.java:129)
	at com.google.gwt.junit.client.GWTTestCase.run(GWTTestCase.java:247)
	at junit.framework.TestSuite.runTest(TestSuite.java:252)
	at junit.framework.TestSuite.run(TestSuite.java:247)
	at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:86)
	at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:367)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:274)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:238)
	at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:161)
	at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:290)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:242)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:121)
Caused by: java.lang.AssertionError
	at com.google.gwt.dev.javac.JdtUtil.signature(JdtUtil.java:369)
	at com.google.gwt.dev.jjs.impl.ReferenceMapper.get(ReferenceMapper.java:121)
	at com.google.gwt.dev.jjs.impl.GwtAstBuilder$AstVisitor.endVisit(GwtAstBuilder.java:641)
	... 50 more
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 7.378 sec <<< FAILURE! - in com.softwareag.gwt.DemoGwtTestCase
testAssertionBug(com.softwareag.gwt.DemoGwtTestCase)  Time elapsed: 7.363 sec  <<< ERROR!

To clarify, assertions in your transpiled java are enabled in SDM, but the JVM assertions (where the compiler is running) are disabled by default in all cases. This test is not using dev mode at all, tests don't run super dev mode to my knowledge, and it isn't configured to run legacy dev mode.

However, when the ltgt gwt-maven-plugin runs, it enables assertions in the JVM and in transpiled code, which causes this apparent false positive (since the code actually works without the assertion) to go off. #9691 is another example of this, a false positive that net.ltgt.gwt.maven:gwt-maven-plugin:test trips by default.

Example workaround, disabling JVM asserts, while still enabling test asserts. Note that you include JVM-only test cases in the same project, they must be run separately, such as with surefire.

      <plugin>
        <groupId>net.ltgt.gwt.maven</groupId>
        <artifactId>gwt-maven-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>test</goal>
            </goals>
            <configuration>
              <!-- disable JVM asserts, but enable JS asserts for tests -->
              <enableAssertions>false</enableAssertions>
              <testArgs>
                <testArg>-checkAssertions</testArg>
              </testArgs>
            </configuration>
          </execution>
        </executions>
      </plugin>

Examining this stack trace in the IDE, the failure mode is quite similar to #9653, and I think the fix is likely similar as well - GWT should detect that this is an intersection type and behave accordingly, rather than just assuming that it isn't. I'll have more time tomorrow to look at this, possibly propose a fix.

Yeah I haven seen that the net.ltgt.gwt.maven:gwt-maven-plugin can also execute tests but in my example project I'm using maven-surefire-plugin with gwt.args system property:

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19.1</version>
            <executions>
                <execution>
                    <id>gwt-unit-tests</id>
                    <phase>test</phase>
                    <goals>
                        <goal>test</goal>
                    </goals>
                    <configuration>
                        <includes>
                            <include>**/*GwtTestCase.java</include>
                        </includes>
                        <systemPropertyVariables>
                            <gwt.args>-devMode</gwt.args>
                        </systemPropertyVariables>
                    </configuration>
                </execution>
            </executions>
        </plugin>

While I suspect that the fix I have in mind will also fix legacy dev mode, at this time I'm not going to spend any concerted effort on ensuring that this is the case: legacy dev mode is expected to be incompatible with jsinterop, java8+ language features, etc.

I am assuming this only happens when running with jvm version > 8. Is that so?

I think the assertion can be removed and constantPoolName() is probably the right choice here (which as far as I can see it is probably the right choice). If you remove the assertion please add 2 tests. One in GwtAstBuilderTest that documents the internal behaviour and one in CompilerTest#testConditionals to assert the runtime behaviour.

BTW what is the intersection type that you are getting with String and nonString, is it Comparable&Serializable?

If so you would need to add a test where you assign to Comparable and another where you assign to Serializable to ensure that order does not matter since constantPoolName() would be the type for the erasure of the intersection IIRC.

I think the assertion can be removed and constantPoolName() is probably the right choice here (which as far as I can see it is probably the right choice). If you remove the assertion please add 2 tests. One in GwtAstBuilderTest that documents the internal behaviour and one in CompilerTest#testConditionals to assert the runtime behaviour.

A more correct solution would be to add the handling in GwtAstBuilder#endVisit(ConditionalExpression x, BlockScope scope), and insert an intersection cast there, similar to what @niloc132 suggested; in that case the assertion should remain.

I am happy to review the code.

@rluble the issue also appears Java 8.

Hmm, could you post a more complete example, something that has all the types and make sure that it also fails. E.g.

TypeA a;
TypeB b;
TypeC c = cond ? a : b:

With the relevant information on the supertypes of A, B and C.

The way we used to experience this bug looks something like

return "SomeThing[name=" + (areNamesEnabled() ? "MyName" : '?');

which fixed with a rewrite to:

return "SomeThing[name=" + (areNamesEnabled() ? "MyName" : "?");

If it matters areNamesEnabled() was effectively a compile time constant.

This appeared in a version somewhere between 2.8.2 and 2.9.0. I haven't checked the latest release of 2.9.0 but I assume this is still present and this is the same as this bug.

We are having similar issues too on GWT 2.9.0 when running our Unit Tests

com.google.gwt.dev.jjs.InternalCompilerException: Error constructing Java AST
	at com.google.gwt.dev.jjs.impl.GwtAstBuilder.translateException(GwtAstBuilder.java:4033)
	at com.google.gwt.dev.jjs.impl.GwtAstBuilder$AstVisitor.endVisit(GwtAstBuilder.java:647)
	at org.eclipse.jdt.internal.compiler.ast.ConditionalExpression.traverse(ConditionalExpression.java:827)
	at org.eclipse.jdt.internal.compiler.ast.BinaryExpression.traverse(BinaryExpression.java:1919)
	at org.eclipse.jdt.internal.compiler.ast.BinaryExpression.traverse(BinaryExpression.java:1919)
	at org.eclipse.jdt.internal.compiler.ast.BinaryExpression.traverse(BinaryExpression.java:1919)
	at org.eclipse.jdt.internal.compiler.ast.BinaryExpression.traverse(BinaryExpression.java:1919)
	at org.eclipse.jdt.internal.compiler.ast.CombinedBinaryExpression.traverse(CombinedBinaryExpression.java:370)
	at org.eclipse.jdt.internal.compiler.ast.MessageSend.traverse(MessageSend.java:1107)
	at org.eclipse.jdt.internal.compiler.ast.MethodDeclaration.traverse(MethodDeclaration.java:365)
	at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.traverse(TypeDeclaration.java:1448)
	at com.google.gwt.dev.jjs.impl.GwtAstBuilder.processImpl(GwtAstBuilder.java:3969)
	at com.google.gwt.dev.jjs.impl.GwtAstBuilder.process(GwtAstBuilder.java:4007)
	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:1020)
	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:423)
	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.junit.JUnitShell.compileForWebMode(JUnitShell.java:1093)
	at com.google.gwt.junit.JUnitShell.maybeCompileForWebMode(JUnitShell.java:1158)
	at com.google.gwt.junit.CompileStrategy.maybeCompileModuleImpl2(CompileStrategy.java:183)
	at com.google.gwt.junit.CompileStrategy.maybeCompileModuleImpl(CompileStrategy.java:113)
	at com.google.gwt.junit.SimpleCompileStrategy.maybeCompileModule(SimpleCompileStrategy.java:36)
	at com.google.gwt.junit.JUnitShell.runTestImpl(JUnitShell.java:1349)
	at com.google.gwt.junit.JUnitShell.runTestImpl(JUnitShell.java:1317)
	at com.google.gwt.junit.JUnitShell.runTest(JUnitShell.java:681)
	at com.google.gwt.junit.client.GWTTestCase.runTest(GWTTestCase.java:421)
	at junit.framework.TestCase.runBare(TestCase.java:141)
	at junit.framework.TestResult$1.protect(TestResult.java:122)
	at junit.framework.TestResult.runProtected(TestResult.java:142)
	at junit.framework.TestResult.run(TestResult.java:125)
	at junit.framework.TestCase.run(TestCase.java:129)
	at com.google.gwt.junit.client.GWTTestCase.run(GWTTestCase.java:247)
	at junit.framework.TestSuite.runTest(TestSuite.java:255)
	at junit.framework.TestSuite.run(TestSuite.java:250)
	at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:84)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: java.lang.AssertionError
	at com.google.gwt.dev.javac.JdtUtil.signature(JdtUtil.java:369)
	at com.google.gwt.dev.jjs.impl.ReferenceMapper.get(ReferenceMapper.java:121)
	at com.google.gwt.dev.jjs.impl.GwtAstBuilder$AstVisitor.endVisit(GwtAstBuilder.java:641)
	... 49 more

com.google.gwt.core.ext.UnableToCompleteException: (see previous log entries)

	at com.google.gwt.junit.JUnitShell.compileForWebMode(JUnitShell.java:1098)
	at com.google.gwt.junit.JUnitShell.maybeCompileForWebMode(JUnitShell.java:1158)
	at com.google.gwt.junit.CompileStrategy.maybeCompileModuleImpl2(CompileStrategy.java:183)
	at com.google.gwt.junit.CompileStrategy.maybeCompileModuleImpl(CompileStrategy.java:113)
	at com.google.gwt.junit.SimpleCompileStrategy.maybeCompileModule(SimpleCompileStrategy.java:36)
	at com.google.gwt.junit.JUnitShell.runTestImpl(JUnitShell.java:1349)
	at com.google.gwt.junit.JUnitShell.runTestImpl(JUnitShell.java:1317)
	at com.google.gwt.junit.JUnitShell.runTest(JUnitShell.java:681)
	at com.google.gwt.junit.client.GWTTestCase.runTest(GWTTestCase.java:421)
	at junit.framework.TestCase.runBare(TestCase.java:141)
	at junit.framework.TestResult$1.protect(TestResult.java:122)
	at junit.framework.TestResult.runProtected(TestResult.java:142)
	at junit.framework.TestResult.run(TestResult.java:125)
	at junit.framework.TestCase.run(TestCase.java:129)
	at com.google.gwt.junit.client.GWTTestCase.run(GWTTestCase.java:247)
	at junit.framework.TestSuite.runTest(TestSuite.java:255)
	at junit.framework.TestSuite.run(TestSuite.java:250)
	at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:84)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

A more correct solution would be to add the handling in GwtAstBuilder#endVisit(ConditionalExpression x, BlockScope scope), and insert an intersection cast there, similar to what @niloc132 suggested; in that case the assertion should remain.

I am happy to review the code.

I am experiencing the same issue using SDM with a construct like "params: " + java.util.Arrays.asList(1, "1", true)
The intersection type for varargs I see is java.lang.Object & Comparable<?> & java.io.Serializable

Caused by: java.lang.AssertionError
	at com.google.gwt.dev.javac.JdtUtil.signature(JdtUtil.java:369)
	at com.google.gwt.dev.jjs.impl.ReferenceMapper.get(ReferenceMapper.java:121)
	at com.google.gwt.dev.jjs.impl.ReferenceMapper.get(ReferenceMapper.java:138)
	at com.google.gwt.dev.jjs.impl.GwtAstBuilder$AstVisitor.popCallArguments(GwtAstBuilder.java:3229)
	at com.google.gwt.dev.jjs.impl.GwtAstBuilder$AstVisitor.endVisit(GwtAstBuilder.java:1536)

I can't seem to find the correct place to insert the intersection cast as I don't really understand the compiler that much.
Is there a chance of fixing this?

Thanks.