Closed scabug closed 13 years ago
Imported From: https://issues.scala-lang.org/browse/SI-2464?orig=1 Reporter: Michael Armbrust (marmbrus) Attachments:
@dragos said: (In r19874) Fixed ticket 2464
@dragos said: (In r19945) Closed #2726 and added test for #2464 (refs #2464)
@paulp said: My new officemates ran into this bug, also as a consequence of code generated by thrift. The test case given in this ticket works, but the bug is definitely still in trunk. I will work on getting a reproduction but please trust me for the moment. BTW incredibly I overlooked this ticket while researching this exact issue, because there are something like a dozen open or should-be-open tickets relating to inner classes.
I am going on a bytecode-tool-writing kick right now. Reconciling all the java and scala factors in the tricky interdependent attribute areas like InnerClasses is simply too hard. We're not going to get it right without validating the result.
@paulp said: See #2896 for what should be a concrete example of this (or failing that, something similar.)
@paulp said: ...and also #2940.
Brian Hsu (brianhsu) said: Just FYI, I think the following is another concrete example of this bug (or similar to this, I'm not very sure).
This is a code that use Google Mpas API in Android. In Google Maps API, MapView class has a static inner class witch extends from android.view.ViewGroup.LayoutParams which in turns another static inner class of ViewGroup.
Here is the sample code:
import android.os.Bundle
import android.widget.LinearLayout
import com.google.android.maps.MapActivity
import com.google.android.maps.MapView
class TestMap extends MapActivity
{
type F = MapView.LayoutParams // This will caused compiler crash
override def isRouteDisplayed () = false
override def onCreate(savedInstanceState: Bundle)
{
val mapView: MapView = null // This line also crash compiler
}
}
When using the following commands to compile
/opt/scala-svn/bin/scalac -cp android.jar:maps.jar Test.scala
The compiler will complain
error: error while loading MapView, Missing dependency 'class android.view.ViewGroup$$LayoutParams', required by /opt/android-sdk-update-manager/add-ons/google_apis-7_r01/libs/maps.jar(com/google/android/maps/MapView.class)
error: error while loading LayoutParams, Missing dependency 'class android.view.ViewGroup$$LayoutParams', required by /opt/android-sdk-update-manager/add-ons/google_apis-7_r01/libs/maps.jar(com/google/android/maps/MapView$$LayoutParams.class)
two errors found
The scala compiler version is svn trunk at 2010-03-26
papamitra said:
Maybe maps.jar (Google Maps API in Android) is broken. Because it seems that maps.jar in Android SDK is created by "mkstubs" in Android source tree(development/tools/mkstubs)
And, it writes in mkstubs's README.txt,
The generated constructors are not proper. They do not invoke the matching super() before the generated throw exception. Any attempt to load such a class should trigger an error from the byte code verifier or the class loader.
I replace MapView.class and MapController.class in maps.jar with the following files.
http://gist.github.com/454723 http://gist.github.com/454727
And, work it.
@paulp said: Replying to [comment:16 papamitra]:
The generated constructors are not proper. They do not invoke the matching super() before the generated throw exception. Any attempt to load such a class should trigger an error from the byte code verifier or the class loader.
Well that's an interesting thing to find out at this stage of the game. Thanks. But even if we exclude maps.jar from consideration there are other examples of the issue with no such excuse.
@michelou said: Case 1 (from marmbrus) is ok with 2.8.0.final.
Case 2 (from brianhsu) is fixed in rev22630. That case did in particular affect Android code (I had myself to disable Android examples such as MapsDemo, Wiktionary, Panoramio, WindWaves, see rev22631).
Stefan Endrullis (xylo) said (edited on Feb 24, 2013 7:50:14 PM UTC): I know this bug is already 2 years old. However, today I got the same error or at least a very similar one using the Scala 2.10.0 and Scala 2.10.1-RC1 compiler: error: error while loading com.vaadin.ui., class file '/home/stefan/.m2/repository/com/vaadin/vaadin/6.8.8/vaadin-6.8.8.jar(com/vaadin/ui/ClientWidget$LoadStyle.class)' is broken
It's easy to reproduce. I bundled a tiny example project into the attached zip archive. To compile it just run $ mvn compile
The only Scala file included in the project is a one-liner: class PortalLayout extends org.vaadin.sasha.portallayout.PortalLayout
Obviously it's not even necessary to refer to the "broken" class directly in the Scala code in order to let the compiler fail. And there is unfortunately nothing I can do to work around this except to avoid the class org.vaadin.sasha.portallayout.PortalLayout in my project completely.
Regards, Stefan
Stefan Endrullis (xylo) said (edited on Apr 25, 2013 4:15:48 PM UTC): Same in 2.10.1 and 2.11.0-M2. The Scala compiler fails to compile the vaadin project (attached).
Scala seems to be no longer compatible with vaadin. That's very disappointing. :(
@paulp said: Stefan, I'll make you a deal - I fixed the bug you're hitting, but I do not have time to dredge out a test case. If you can manage the test case (no maven, no downloading giant jars - small self-contained java and scala) you'll see this fixed in 2.10.2.
Here's the code part: https://github.com/paulp/scala/tree/issue/2464
John Nestor (nestorpersist) said: I am not sure about building a test case without the Vaadin jar. Although the best guess is that Java inner classes are involved I don't know how to easily extract all the potentially problematic code out of Vaadin.
If you have a fix I would be happy to try out your snapshot scala compiler with the fix on my project if you can make it available.
@paulp said: I am not sure what you mean by "make it available" but the code is right there. I know it fixes the problem; I need a test case not to convince myself it works but to get the code into the compiler.
Stefan Endrullis (xylo) said (edited on May 2, 2013 10:35:15 AM UTC): I already tried to write a small test case with the inner class that could not be parsed by scalac (ClientWidget$LoadStyle), but I did not manage to reproduce the error that occurs in the example vaadin project. It seems to be complicated. I bundled a smaller test project with one Scala file and two jar files (vaadin and portletlayout). I attached it as tiny-vaadin.tar.bz2.
Compilation: scalac -classpath vaadin-6.8.8.jar:portallayout-1.3.1.jar PortalLayout.scala
Stefan Endrullis (xylo) said: smaller vaadin project without maven
@retronym said: I've debugged this.
With Paul's patch in place to change this from a crasher to a warning, we get:
sym = {scala.reflect.internal.Symbols$TermSymbol@4573}"value EAGER"
sym.owner = {scala.reflect.internal.Symbols$ModuleClassSymbol@4580}"module class ClientWidget$LoadStyle"
sym.owner.owner = {scala.reflect.internal.Symbols$PackageClassSymbol@4582}"package ui"
sym.owner.owner.info.decl(this.global.newTermName("ClientWidget")).info.decls.toList(1).moduleClass
= {scala.reflect.internal.Symbols$ModuleClassSymbol@5792}"module class LoadStyle"
I think the root bug is the existence of module class ClientWidget$LoadStyle
.
I haven't managed yet to distill the essence of Stefan's test case to something independent from those JARs.
@retronym said (edited on Jun 6, 2013 4:02:06 PM UTC):
Stefan's problem with Vaadin appears to stem from a deficient InnerClasses
section in org.vaadin.sasha.portallayout.PortalLayout
% qbin/scala -Ydebug -classpath /Users/jason/Downloads/tiny-vaadin/vaadin-6.8.8.jar:/Users/jason/Downloads/tiny-vaadin/portallayout-1.3.1.jar:.
% curl --silent https://vaadin-portal-layout.googlecode.com/svn-history/r10/trunk/src/org/vaadin/sasha/portallayout/PortalLayout.java | head -25
package org.vaadin.sasha.portallayout;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.vaadin.sasha.portallayout.client.ui.VPortalLayout;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.ui.AbstractLayout;
import com.vaadin.ui.ClientWidget;
import com.vaadin.ui.Component;
import com.vaadin.ui.ClientWidget.LoadStyle;
/**
* Layout that presents its contents in a portal style.
* @author p4elkin
*/
@SuppressWarnings("serial")
@ClientWidget(value = VPortalLayout.class, loadStyle = LoadStyle.EAGER)
public class PortalLayout extends AbstractLayout {
scala> :javap org.vaadin.sasha.portallayout.PortalLayout
Compiled from "PortalLayout.java"
public class org.vaadin.sasha.portallayout.PortalLayout extends com.vaadin.ui.AbstractLayout implements com.vaadin.ui.Layout$SpacingHandler,com.vaadin.event.LayoutEvents$LayoutClickNotifier
SourceFile: "PortalLayout.java"
RuntimeVisibleAnnotations: length = 0x12
00 01 02 54 00 02 02 55 63 02 56 02 57 65 02 58
02 59
InnerClass:
public #605= #132 of #603; //LayoutClickEvent=class com/vaadin/event/LayoutEvents$LayoutClickEvent of class com/vaadin/event/LayoutEvents
public abstract #606= #135 of #603; //LayoutClickListener=class com/vaadin/event/LayoutEvents$LayoutClickListener of class com/vaadin/event/LayoutEvents
public abstract #607= #7 of #603; //LayoutClickNotifier=class com/vaadin/event/LayoutEvents$LayoutClickNotifier of class com/vaadin/event/LayoutEvents
public #610= #608 of #176; //Event=class com/vaadin/ui/Component$Event of class com/vaadin/ui/Component
public abstract #613= #5 of #611; //SpacingHandler=class com/vaadin/ui/Layout$SpacingHandler of class com/vaadin/ui/Layout
public abstract #614= #468 of #63; //Entry=class java/util/Map$Entry of class java/util/Map
public abstract #617= #615 of #1; //PortletCloseListener=class org/vaadin/sasha/portallayout/PortalLayout$PortletCloseListener of class org/vaadin/sasha/portallayout/PortalLayout
public #618= #91 of #1; //PortletClosedEvent=class org/vaadin/sasha/portallayout/PortalLayout$PortletClosedEvent of class org/vaadin/sasha/portallayout/PortalLayout
public #619= #106 of #1; //PortletCollapseEvent=class org/vaadin/sasha/portallayout/PortalLayout$PortletCollapseEvent of class org/vaadin/sasha/portallayout/PortalLayout
public abstract #622= #620 of #1; //PortletCollapseListener=class org/vaadin/sasha/portallayout/PortalLayout$PortletCollapseListener of class org/vaadin/sasha/portallayout/PortalLayout
minor version: 0
major version: 50
Constant pool:
...
const #596 = Asciz Lcom/vaadin/ui/ClientWidget;;
const #597 = Asciz value;
const #598 = Asciz Lorg/vaadin/sasha/portallayout/client/ui/VPortalLayout;;
const #599 = Asciz loadStyle;
const #600 = Asciz Lcom/vaadin/ui/ClientWidget$LoadStyle;;
const #601 = Asciz EAGER;
const #602 = Asciz InnerClasses;
const #603 = class #604; // com/vaadin/event/LayoutEvents
...
I tried to compile a stripped down version of that class:
package test;
import com.vaadin.ui.AbstractLayout;
import com.vaadin.ui.ClientWidget;
import com.vaadin.ui.ClientWidget.LoadStyle;
@ClientWidget(value = com.vaadin.terminal.gwt.client.Paintable.class, loadStyle = LoadStyle.EAGER)
public abstract class PortalLayout extends AbstractLayout {
}
% javac -version javac 1.6.0_37
% qbin/scala -Ydebug -classpath /Users/jason/Downloads/tiny-vaadin/vaadin-6.8.8.jar:/Users/jason/Downloads/tiny-vaadin/portallayout-1.3.1.jar:.
scala> :javap test/PortalLayout
Compiled from "PortalLayout.java"
public abstract class test.PortalLayout extends com.vaadin.ui.AbstractLayout
SourceFile: "PortalLayout.java"
RuntimeVisibleAnnotations: length = 0x12
00 01 00 0B 00 02 00 0C 63 00 0D 00 0E 65 00 12
00 13
InnerClass:
public final #16= #15 of #23; //LoadStyle=class com/vaadin/ui/ClientWidget$LoadStyle of class com/vaadin/ui/ClientWidget
minor version: 0
major version: 50
Constant pool:
const #1 = Method #3.#20; // com/vaadin/ui/AbstractLayout."<init>":()V
const #2 = class #21; // test/PortalLayout
const #3 = class #22; // com/vaadin/ui/AbstractLayout
const #4 = Asciz <init>;
const #5 = Asciz ()V;
const #6 = Asciz Code;
const #7 = Asciz LineNumberTable;
const #8 = Asciz SourceFile;
const #9 = Asciz PortalLayout.java;
const #10 = Asciz RuntimeVisibleAnnotations;
const #11 = Asciz Lcom/vaadin/ui/ClientWidget;;
const #12 = Asciz value;
const #13 = Asciz Lcom/vaadin/terminal/gwt/client/Paintable;;
const #14 = Asciz loadStyle;
const #15 = class #24; // com/vaadin/ui/ClientWidget$LoadStyle
const #16 = Asciz LoadStyle;
const #17 = Asciz InnerClasses;
const #18 = Asciz Lcom/vaadin/ui/ClientWidget$LoadStyle;;
const #19 = Asciz EAGER;
const #20 = NameAndType #4:#5;// "<init>":()V
const #21 = Asciz test/PortalLayout;
const #22 = Asciz com/vaadin/ui/AbstractLayout;
const #23 = class #25; // com/vaadin/ui/ClientWidget
const #24 = Asciz com/vaadin/ui/ClientWidget$LoadStyle;
const #25 = Asciz com/vaadin/ui/ClientWidget;
The JAR file containing the class in question contains:
META-INF:
Manifest-Version: 1.0
Implementation-Title: PortalLayout
Implementation-Version: 1.3.1
Vaadin-Package-Version: 1
Class-Path:
Vaadin-Widgetsets: org.vaadin.sasha.portallayout.PortallayoutWidgetset
rebel.xml
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://www.zeroturnaround.com/alderaan/rebel-2_0.xsd">
<classpath>
<dir name="/Users/p4elkin/Projects/PortalLayout/build/classes">
</dir>
</classpath>
<web>
<link target="/">
<dir name="/Users/p4elkin/Projects/PortalLayout/WebContent">
</dir>
</link>
</web>
</application>
The repository doesn't have an Ant/Maven build or the like. Not sure what Java compiler generated that bytecode, nor which tools had their way with it. (This is all GWT related, maybe that gets involved.)
The JVM spec offers:
The InnerClasses attribute is a variable-length attribute in the attributes table of a ClassFile structure (§4.1). If the constant pool of a class or interface C contains a CONSTANT_Class_info entry which represents a class or interface that is not a member of a package, then C's ClassFile structure must have exactly one InnerClasses attribute in its attributes table.
...
If a class has members that are classes or interfaces, its constant_pool table (and hence its InnerClasses attribute) must refer to each such member, even if that member is not otherwise mentioned by the class. These rules imply that a nested class or interface member will have
InnerClasses information for each enclosing class and for each immediate member.
Long story short, I'll submit Paul's patch to make us resilient against such bytecode.
@retronym said: https://github.com/scala/scala/pull/2639
Hassan Sultan (hassan.sultan) said: I encountered a similar issue that I think the fix above will not address, I think it is related to an SBT bug (https://github.com/sbt/sbt/issues/987), here is a minimal repro: https://github.com/TheSultan/sbtivypublicationsbug
The Scala compiler gives an erroneous 'class file broken' error when trying to use classes that inherit from static Java inner classes. This is particularly a problem when integrating with Java code generated by the [http://incubator.apache.org/thrift/ Apache Thrift] compiler. Below are the files and steps needed to reproduce the problem.
!ClassOne.java:
!ClassTwo.java:
!ScalaClasses.scala:
Bug.scala:
And the following compilation sequence:
The compilation of Bug.scala will fail with the following error on 2.7.5:
And this error on 2.8.0-b20090919135825: