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
Irregular class-files generated for InnerClasses #2749
Comments
Imported From: https://issues.scala-lang.org/browse/SI-2749?orig=1 |
@jrudolph said: I would guess, the InnerClasses attribute is mainly used in the Java compiler (to recreate scopes and visibility when the source isn't available any more). Therefore, fixing this issue consequently hides from the Java compiler anonymous classes and functions generated by Scala. However, right now, I cannot come up with a useful example how to use the metadata in EnclosingMethod. The duplicate InnerClass entry might confuse some tools like ProGuard. Maybe the whole issue is rather cosmetical and can wait for concrete problems. |
@paulp said: Test$$$$anonfun$$testFunc$$1 cannot be generated as an anonymous inner class unless there is to be a class called Test$$$$anonfun$$testFunc, which you will notice, there is not. The java reflection implementation takes it as a given that after stripping the name of the enclosing class, an anonymous class matches the regex $$[0-9]+ and a local class that plus a simple name. That is definitely not cosmetic: the implementation depends on it. The EnclosingMethod attribute can be generated if and only if the inner class is local or anonymous. Since we can't generate valid classes of those kinds according to java's definition without an encoding change, we can't generate the attribute either. This is all a direct consequence of trying to use '$$' to do too much. I don't know what can be done about that. As to where the inner class goes, at the moment I think you're right it should and could be in the implementation class, but it requires further study. |
@dragos said:
Having said that, I don't have any strong objections to changing these naming conventions. I just don't see any obvious way to satisfy all requirements and the effort seems disproportionate to the gains. It boils down to two questions:
or in more general terms: should we revamp our naming conventions to satisfy Java binary compatibility guidelines? There's enough meat for a SIP here, but I am very time constrained. I would happily comment/review/contribute if someone wants to take over. |
Charles Oliver Nutter (headius) said: No class Scala delivers should ever break Java reflection. For now we are patching around it by catching the NoClassDefFoundError and giving up on inspecting inner classes for the class in question. Class.java:-2:in `getDeclaredClasses0': java.lang.NoClassDefFoundError: scala/collection/mutable/ArrayBuffer$$$$anonfun$$remove$$1
from Class.java:1699:in `getDeclaredClasses'
from JavaClass.java:1848:in `getDeclaredClasses'
from JavaClass.java:695:in `installClassClasses'
from JavaClass.java:615:in `setupProxy'
from Java.java:506:in `createProxyClass'
from Java.java:445:in `getProxyClass'
from Java.java:487:in `get_proxy_class'
from JavaUtilities.java:34:in `get_proxy_class'
from org/jruby/javasupport/JavaUtilities$$s_method_1_0$$RUBYINVOKER$$get_proxy_class.gen:65535:in `call'
from CachingCallSite.java:312:in `cacheAndCall'
from CachingCallSite.java:151:in `call'
from CallOneArgNode.java:57:in `interpret'
from LocalAsgnNode.java:123:in `interpret'
from NewlineNode.java:103:in `interpret'
from WhenOneArgNode.java:36:in `whenSlowTest'
from WhenOneArgNode.java:46:in `when'
from CaseNode.java:133:in `interpret'
from NewlineNode.java:103:in `interpret'
from BlockNode.java:71:in `interpret'
from ASTInterpreter.java:64:in `INTERPRET_METHOD'
from InterpretedMethod.java:184:in `call'
from DefaultMethod.java:177:in `call'
from CachingCallSite.java:312:in `cacheAndCall'
from CachingCallSite.java:151:in `call'
from FCallOneArgNode.java:36:in `interpret'
from NewlineNode.java:103:in `interpret'
from BlockNode.java:71:in `interpret'
from RootNode.java:129:in `interpret'
from ASTInterpreter.java:101:in `INTERPRET_ROOT'
from Ruby.java:721:in `runInterpreter'
from Ruby.java:582:in `runNormally'
from Ruby.java:418:in `runFromMain'
from Main.java:286:in `run'
from Main.java:128:in `run'
from Main.java:97:in `main'
Caused by:
URLClassLoader.java:202:in `run': java.lang.ClassNotFoundException: scala.collection.mutable.ArrayBuffer$$$$anonfun$$remove$$1
from AccessController.java:-2:in `doPrivileged'
from URLClassLoader.java:190:in `findClass'
from JRubyClassLoader.java:49:in `findClass'
from ClassLoader.java:307:in `loadClass'
from ClassLoader.java:248:in `loadClass'
from Class.java:-2:in `getDeclaredClasses0'
from Class.java:1699:in `getDeclaredClasses'
from JavaClass.java:1848:in `getDeclaredClasses'
from JavaClass.java:695:in `installClassClasses'
from JavaClass.java:615:in `setupProxy'
from Java.java:506:in `createProxyClass'
from Java.java:445:in `getProxyClass'
from Java.java:487:in `get_proxy_class'
from JavaUtilities.java:34:in `get_proxy_class'
from org/jruby/javasupport/JavaUtilities$$s_method_1_0$$RUBYINVOKER$$get_proxy_class.gen:65535:in `call'
from CachingCallSite.java:312:in `cacheAndCall'
from CachingCallSite.java:151:in `call'
from CallOneArgNode.java:57:in `interpret'
from LocalAsgnNode.java:123:in `interpret'
from NewlineNode.java:103:in `interpret'
from WhenOneArgNode.java:36:in `whenSlowTest'
from WhenOneArgNode.java:46:in `when'
from CaseNode.java:133:in `interpret'
from NewlineNode.java:103:in `interpret'
from BlockNode.java:71:in `interpret'
from ASTInterpreter.java:64:in `INTERPRET_METHOD'
from InterpretedMethod.java:184:in `call'
from DefaultMethod.java:177:in `call'
from CachingCallSite.java:312:in `cacheAndCall'
from CachingCallSite.java:151:in `call'
from FCallOneArgNode.java:36:in `interpret'
from NewlineNode.java:103:in `interpret'
from BlockNode.java:71:in `interpret'
from RootNode.java:129:in `interpret'
from ASTInterpreter.java:101:in `INTERPRET_ROOT'
from Ruby.java:721:in `runInterpreter'
from Ruby.java:582:in `runNormally'
from Ruby.java:418:in `runFromMain'
from Main.java:286:in `run'
from Main.java:128:in `run'
from Main.java:97:in `main' |
@paulp said:
I'm with you on both points (as you can see above where I say it's more than cosmetic) but as to the second point, note that #2083 was closed wontfix and with the indication that all bets are off for classes with a $$ in them, which of course is almost all of them. I find I can no longer crash things in the same way, because classOf denies all knowledge of the trait implementation class - I wonder when that changed. |
Charles Oliver Nutter (headius) said: It's also probably valid to say that the JDK is too sensitive to class names in these cases, but of course we're not going to get the JDK to fix this in any reasonable timeframe, so we have to be good citizens and not do things that break it. |
@dragos said:
I don't know exactly when it changed, but I can point out the code that does that. It's the DefaultJavaContext in the ClassPath abstractions that filters out classes ending in "$$class". That being said, I am open to any suggestions as to how to fix this. |
@dragos said: |
This is a follow-up to #1167 which was a problem with the !InnerClass attribute of anonymous classes which resulted in runtime exceptions. Whereas this particular problem was fixed, the generated class-files do still not comply to the JVM spec.
Take again this example (compiled with scala-2.8.0.r19928-b20091129020233):
The compiler generates three classes
Test
,Test$$class
andTest$$$$anonfun$$testFunc$$1
.Here are the relevant parts from the
javap -v
outputs:#1167 was about
Test
andTest$$$$anonfun$$testFunc$$1
inconsistently reporting the outer class. This was obviously fixed. But there still remain several issues:Test$$class
because that contains the implementation.Test$$class
andTest
both contain the !InnerClass attributeIf C is anonymous, the value of the inner_name_index item must be zero.
and
If C is not a member, the value of the outer_class_info_index item must be zero.
Class.getEnclosingMethod/Constructor
are not working.As a reference, see a corresponding Java example:
compiled with
javac -source 1.5 -target 1.5 Tester.java
And the
javap
output:See this [http://old.nabble.com/Re%3A-Scala-Interpreter-Oddity----General-Class-Name-Curio-p25869373.html mailing-list post], as well, for more information.
The text was updated successfully, but these errors were encountered: