Wednesday, December 30, 2015

ArrayIndexOutOfBoundsException in GWT Compiler for non-trivial type arg in RPC service parameter

Hi there,

we've been encountering a strange compiler error when making special use of generics in GWT RPC service parameters. I've tried to boil it down to an example that you can find here as a ready-to-fail Eclipse project: https://drive.google.com/file/d/0BzPFwCC87Ht6VVVoQ0hjY3BoREU/view?usp=sharing

The exception the compiler throws is this:

Compiling module gwt.generics.bug.Gwt_generics_bug
   Computing all possible rebind results for 'gwt.generics.bug.client.GreetingService'
      Rebinding gwt.generics.bug.client.GreetingService
         Invoking generator com.google.gwt.user.rebind.rpc.ServiceInterfaceProxyGenerator
            [ERROR] Generator 'com.google.gwt.user.rebind.rpc.ServiceInterfaceProxyGenerator' threw an exception while rebinding 'gwt.generics.bug.client.GreetingService'
java.lang.ArrayIndexOutOfBoundsException: 1
at com.google.gwt.user.rebind.rpc.TypeParameterExposureComputer.getFlowInfo(TypeParameterExposureComputer.java:420)
at com.google.gwt.user.rebind.rpc.TypeParameterExposureComputer.access$200(TypeParameterExposureComputer.java:40)
at com.google.gwt.user.rebind.rpc.TypeParameterExposureComputer$TypeParameterFlowInfo.getFlowInfo(TypeParameterExposureComputer.java:322)
at com.google.gwt.user.rebind.rpc.TypeParameterExposureComputer$TypeParameterFlowInfo.recordCausesExposure(TypeParameterExposureComputer.java:329)
at com.google.gwt.user.rebind.rpc.TypeParameterExposureComputer$TypeParameterFlowInfo.computeIndirectExposureCauses(TypeParameterExposureComputer.java:269)
at com.google.gwt.user.rebind.rpc.TypeParameterExposureComputer$TypeParameterFlowInfo.initializeExposure(TypeParameterExposureComputer.java:195)
at com.google.gwt.user.rebind.rpc.TypeParameterExposureComputer$TypeParameterFlowInfo.updateFlowInfo(TypeParameterExposureComputer.java:169)
at com.google.gwt.user.rebind.rpc.TypeParameterExposureComputer.computeTypeParameterExposure(TypeParameterExposureComputer.java:398)
at com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.getFlowInfo(SerializableTypeOracleBuilder.java:1373)
at com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.getTypeParameterExposure(SerializableTypeOracleBuilder.java:1006)
at com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.checkTypeArgument(SerializableTypeOracleBuilder.java:1330)
at com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.checkSubtype(SerializableTypeOracleBuilder.java:1176)
at com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.checkSubtypes(SerializableTypeOracleBuilder.java:1262)
at com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.computeTypeInstantiability(SerializableTypeOracleBuilder.java:994)
at com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.checkTypeArgument(SerializableTypeOracleBuilder.java:1337)
at com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.checkSubtype(SerializableTypeOracleBuilder.java:1176)
at com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.checkSubtypes(SerializableTypeOracleBuilder.java:1262)
at com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.computeTypeInstantiability(SerializableTypeOracleBuilder.java:994)
at com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.build(SerializableTypeOracleBuilder.java:792)
at com.google.gwt.user.rebind.rpc.ProxyCreator.create(ProxyCreator.java:324)
at com.google.gwt.user.rebind.rpc.ServiceInterfaceProxyGenerator.generateIncrementally(ServiceInterfaceProxyGenerator.java:67)
at com.google.gwt.dev.javac.StandardGeneratorContext.runGeneratorIncrementally(StandardGeneratorContext.java:754)
at com.google.gwt.dev.cfg.RuleGenerateWith.realize(RuleGenerateWith.java:160)
at com.google.gwt.dev.shell.StandardRebindOracle$Rebinder.rebind(StandardRebindOracle.java:79)
at com.google.gwt.dev.shell.StandardRebindOracle.rebind(StandardRebindOracle.java:276)
at com.google.gwt.dev.shell.StandardRebindOracle.rebind(StandardRebindOracle.java:265)
at com.google.gwt.dev.DistillerRebindPermutationOracle.getAllPossibleRebindAnswers(DistillerRebindPermutationOracle.java:87)
at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.createStaticRebindExpression(UnifyAst.java:485)
at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.createRebindExpression(UnifyAst.java:443)
at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.handleMagicMethodCall(UnifyAst.java:576)
at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.endVisit(UnifyAst.java:306)
at com.google.gwt.dev.jjs.ast.JMethodCall.traverse(JMethodCall.java:248)
at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:381)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:293)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:285)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:128)
at com.google.gwt.dev.jjs.ast.JExpressionStatement.traverse(JExpressionStatement.java:42)
at com.google.gwt.dev.jjs.ast.JModVisitor$ListContext.traverse(JModVisitor.java:95)
at com.google.gwt.dev.jjs.ast.JModVisitor.acceptWithInsertRemove(JModVisitor.java:351)
at com.google.gwt.dev.jjs.ast.JBlock.traverse(JBlock.java:92)
at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:381)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:293)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:149)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:145)
at com.google.gwt.dev.jjs.ast.JMethodBody.traverse(JMethodBody.java:83)
at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:381)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:293)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:285)
at com.google.gwt.dev.jjs.ast.JMethod.visitChildren(JMethod.java:600)
at com.google.gwt.dev.jjs.ast.JMethod.traverse(JMethod.java:569)
at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:381)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:293)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:285)
at com.google.gwt.dev.jjs.impl.UnifyAst.mainLoop(UnifyAst.java:1505)
at com.google.gwt.dev.jjs.impl.UnifyAst.exec(UnifyAst.java:870)
at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler$Precompiler.unifyJavaAst(JavaToJavaScriptCompiler.java:1305)
at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler$Precompiler.constructJavaAst(JavaToJavaScriptCompiler.java:1038)
at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler$Precompiler.precompile(JavaToJavaScriptCompiler.java:954)
at com.google.gwt.dev.jjs.MonolithicJavaToJavaScriptCompiler.precompile(MonolithicJavaToJavaScriptCompiler.java:303)
at com.google.gwt.dev.jjs.JavaScriptCompiler.precompile(JavaScriptCompiler.java:38)
at com.google.gwt.dev.Precompile.precompile(Precompile.java:286)
at com.google.gwt.dev.Precompile.precompile(Precompile.java:229)
at com.google.gwt.dev.Precompile.precompile(Precompile.java:145)
at com.google.gwt.dev.Compiler.run(Compiler.java:206)
at com.google.gwt.dev.Compiler.run(Compiler.java:158)
at com.google.gwt.dev.Compiler$1.run(Compiler.java:120)
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:127)
   [ERROR] Errors in 'gwt/generics/bug/client/Gwt_generics_bug.java'
      [ERROR] Line 14: Failed to resolve 'gwt.generics.bug.client.GreetingService' via deferred binding

The issue occurs with version 2.7.0. I haven't gotten around to testing the same with the 2.8.0 snapshot that is available so far. It would be great if someone could try the project in that context.

To reproduce, I've started a GWT project from scratch in Eclipse, letting the project wizard do its thing, then adjusted the service method as follows:

    void useList(List<Pair<String, Boolean>> list);

Here is the Pair type signature:

    public class Pair<A, B> implements IsSerializable { ... }

with the usual, trivial implementation. The entry point is as simple as possible:

    public class Gwt_generics_bug implements EntryPoint {
        public void onModuleLoad() {
            GWT.create(GreetingService.class);
        }
    }

So far, this seems not special at all, and it is hard to see why the compiler would throw up on this. However, I debugged the compiler run (adding -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y to the VM arguments of the compiler launch) and found out that the compiler is having trouble with some seemingly unrelated type which happens to subclass Pair<A, B> as follows:

    public class SpecialPair<A extends BoundsInterface> extends Pair<TwoTypeArgInterface<?, A>, A> {
        public SpecialPair(TwoTypeArgInterface<?, A> a, A b) {
           super(a, b);
        }
    }

BoundsInterface is an empty marker interface, and TwoTypeArgInterface looks like this:

    public interface TwoTypeArgInterface<A extends InterfaceWithOneTypeArg<B>, B extends BoundsInterface> {}

and

    public interface InterfaceWithOneTypeArg<A extends BoundsInterface> {}

The compiler debug session suggests that the compiler confuses the formal parameter "A" of class SpecialPair, occurring with ordinal 0, with its use as actual type parameter in the instantiation of TwoTypeArgInterface where it occurs with index 1. This index 1 is then used to look up the formal parameter in SpecialPair's type parameter list, leading to the ArrayIndexOutOfBoundsException above.

I hope someone from the compiler team could have a look.

In the meantime, we have worked around the issue by more or less repeating the Pair<A, B> implementation for the SpecialPair use case such that subclassing the two-type-params class Pair<A, B> is not necessary for the SpecialPair implementation anymore. This lets the compile succeed for us. Still, I'm of the opinion that this is a compiler bug that should be fixed, if it isn't already in 2.8.0 in which case this would only be "for the record" ;-).

Thanks and best,
-- Axel

--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsubscribe@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment