Details

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)