Details
-
Type:
Defect
-
Status:
Open
-
Resolution: Unresolved
-
Affects Version/s: None
-
Fix Version/s: Backlog
-
Component/s: None
-
Labels:None
Description
Reported by cemer...@snowtide.com, Feb 10, 2009 The jvm has certain implementation limits around the maximum size of classfiles, literal strings, method length, etc; however, in certain circumstances, the Clojure compiler can currently emit classfiles that violate some of those limitations, causing an error later when the classfile is loaded. While test coverage would necessarily detect this sort of problem on a project-by-project basis when one's tests attempted to load a project's classfiles, it seems like Clojure should do the following to ensure failure as quickly as possible: - throw an exception immediately if, while compiling a lib, it is detected that the resulting classfile(s) would violate any classfile implementation limits. Ideally, the exception's message would detail what file and on which line number the offending form is (e.g. if a method's bytecode would be too long). I can imagine that doing this may not be straightforward; a reasonable stop-gap would be for the compiler to immediately attempt to load the generated classfile in order to ensure up-front failure. - emit a warning if any clojure form is read that would, upon being compiled, require violating any of the classfile implementation limits; I suspect that *most* people looking to generate classfiles would be doing so in a "build" environment (rather than loading some code, tinkering, and then using clojure.core/compile), but for those that aren't, I can imagine there being a good deal of frustration around seeing that loading and using some code successfully would eventually produce unusable classfiles. I've appended a sample stack trace emitted by java when it attempted to load a too-long method implementation (which was produced by embedding a large list literal in a compiled lib). Exception in thread "main" java.lang.ClassFormatError: Invalid method Code length 105496 in class file com/foo/MyClass__init at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:675) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124) at java.net.URLClassLoader.defineClass(URLClassLoader.java:260) at java.net.URLClassLoader.access$000(URLClassLoader.java:56) at java.net.URLClassLoader$1.run(URLClassLoader.java:195) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:316) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java: 288) at java.lang.ClassLoader.loadClass(ClassLoader.java:251) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java: 374) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:247) at clojure.lang.RT.loadClassForName(RT.java:1512) at clojure.lang.RT.load(RT.java:394) at clojure.lang.RT.load(RT.java:374) at clojure.core$load__4911$fn__4913.invoke(core.clj:3623) at clojure.core$load__4911.doInvoke(core.clj:3622) at clojure.lang.RestFn.invoke(RestFn.java:413) at clojure.core$load_one__4863.invoke(core.clj:3467) at clojure.core$compile__4918$fn__4920.invoke(core.clj:3633) at clojure.core$compile__4918.invoke(core.clj:3632) at clojure.lang.Var.invoke(Var.java:336) at clojure.lang.Compile.main(Compile.java:56)
richhickey said: Updating tickets (#8, #19, #30, #31, #126, #17, #42, #47, #50, #61, #64, #69, #71, #77, #79, #84, #87, #89, #96, #99, #103, #107, #112, #113, #114, #115, #118, #119, #121, #122, #124)