Welcome! Please see the About page for a little more info on how this works.

0 votes
in Clojure by

Problem:
When an imported class is used as a value, the emitted bytecode uses {{RT.classForName}} to obtain the Class object, causing the class to be loaded and static initializers to be executed. This is different from when (link: http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4.1 text: Java calls static initializers) and makes it more difficult to use clojure with code that depend on the Java semantics.

Motivation
Some code has static initializers that can only execute in a certain environment. A prime example is JavaFX, where many JavaFX classes require the JavaFX platform to be started before any static initializers can run.

Consider this code:

`
(import 'javafx.scene.control.Cell)

(defn f [] Cell)
`

It currently can't be compiled and executed (for example with "{{clj example.clj}}" using clojure-1.9.0beta2) failing with a {{CompilerException java.lang.ExceptionInInitializerError}}, with the root cause "Toolkit not initialized". This use of {{Cell}} as the return value of {{f}} causes the class to be loaded and initialised.

Approach
Modify ObjExpr.emitValue to emit a call to {{RT.classForNameNonLoading}} instead of {{RT.classForName}} when the value being emitted is a Class.

Patch
https://dev.clojure.org/jira/secure/attachment/17426/CLJ-2250-avoid-initializing-class-when-used-as-value.patch

Prior art
The {{import}} form previously was changed to similarly not load the class ((link: https://dev.clojure.org/jira/browse/CLJ-1315 text: CLJ-1315)) and (link: https://dev.clojure.org/jira/browse/CLJ-1743 text: CLJ-1743) attempts to address similar issues where Clojure differs from the Java semantics.

2 Answers

0 votes
by

Comment made by: ragge

Added patch.

0 votes
by
Reference: https://clojure.atlassian.net/browse/CLJ-2250 (reported by ragge)
...