RuntimeException: Can't embed object in code, maybe print-dup not defined

2,712 views
Skip to first unread message

Jeffrey Schwab

unread,
Mar 11, 2011, 7:37:08 PM3/11/11
to clo...@googlegroups.com
What does eval'ing a form containing an object reference cause an exception?  What am I doing wrong?

user=> (eval [(java.util.Date.)])
java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: Fri Mar 11 19:33:04 EST 2011 (NO_SOURCE_FILE:153)

This is the simplest example could contrive, but in the real code, I'm trying to rename a bunch of files.  I have two sequences of File objects; I build up a list of commands in a do block, then try to eval it.  I am using eval because I want to inspect the whole form at the REPL before evaluating it.

user=> (eval [1])
[1]
user=> (eval (java.util.Date.))
#<Date Fri Mar 11 19:31:33 EST 2011>

user=> (doc print-dup)
-------------------------
clojure.core/print-dup
nil
  nil
nil

Armando Blancas

unread,
Mar 11, 2011, 11:12:27 PM3/11/11
to Clojure
You need to quote the vector so the date will be created inside eval,
instead of having the date go into eval as an element of the
(evaluated) vector.

user=> (eval '[(java.util.Date.)])
[#<Date Fri Mar 11 19:59:31 PST 2011>]

Stuart Sierra

unread,
Mar 11, 2011, 11:52:10 PM3/11/11
to clo...@googlegroups.com
`eval` invokes the Clojure compiler, which transforms data structures into Java bytecode.  The Clojure compiler understands Clojure data structures like lists, vectors, and symbols, plus a few Java types like String and numbers.  It doesn't know what to do with a java.util.Date.  "Can't embed object in code" is the compiler telling you "I don't know what to do with this."

`print-dup` is an internal multimethod used by the Clojure compiler to transform complex objects, like sorted sets, into data structures representing the Clojure code to construct them.  For example,

user=>  (print-dup (sorted-set) *out*)
#=(clojure.lang.PersistentTreeSet/create [])

The #= is a Clojure reader macro that means "evaluate the following code."

The error message "maybe print-dup not defined" is suggesting that, if you really did mean to stick a java.util.Date in your compiled code, you have to provide your own implementation of print-dup.  Specifically, you need a method of print-dup that takes a java.util.Date and returns a data structure representing the Clojure code that *creates* a java.util.Date.  For example:

user=>  (defmethod print-dup java.util.Date [d stream]
          (.write stream "#=(java.util.Date. ")
          (.write stream (str (.getTime d)))
          (.write stream ")"))

user=>  (eval [(java.util.Date.)])
[#<Date Fri Mar 11 23:47:13 EST 2011>]

However, this is probably not what you really meant to do.  More likely, you have a macro or `eval` somewhere that is using code that is incorrectly quoted.

-Stuart Sierra

Ken Wesson

unread,
Mar 11, 2011, 11:58:26 PM3/11/11
to clo...@googlegroups.com
On Fri, Mar 11, 2011 at 11:12 PM, Armando Blancas
<armando...@yahoo.com> wrote:
> You need to quote the vector so the date will be created inside eval,
> instead of having the date go into eval as an element of the
> (evaluated) vector.

A more interesting question is "why does this error exist in the first
place"? Why doesn't eval (or macro expansion) accept general objects
in the input rather than just a few specific types (strings, numbers,
keywords, lists, sets, maps, vectors, etc.)? There are use cases where
it would be quite convenient if all objects that presently produced
that error were instead self-evaluating in the manner of numbers,
strings, and keywords. For one thing, as an optimization you could
precompute some expensive-to-construct object and directly embed it in
compiled code instead of having to calculate it at runtime, or muck
about with computing it, saving it out to a file, reading it on
startup, and adding all the file and directory configuration options
that start to be needed as soon as your app has external data files it
depends on.

Possibly-related (and sometimes equally inconvenient) is that actual
compiled Clojure function objects do *not* produce that error, and
work if they are not closures, but produce cryptic errors when
subsequently invoked if they *are* closures, as though they got copied
without the closed-over values or something instead of just embedded
directly.

Here is the weird behavior of function objects in eval (same result in
macro expansions, when the macro-generated code is actually run):

user=> (def f (let [x 10] (fn [] x))) ; f is a closure
#'user/f
user=> (f) ; works when directly called at repl
10
user=> (eval '((let [x 10] (fn [] x)))) ; create the closure and call
it inside eval -- works
10
user=> (eval f) ; eval the closure itself -- returns the function object
#<user$fn__1429$fn__1430 user$fn__1429$fn__1430@123b9c1>
user=> (eval '(f)) ; eval can call the function through its var, and it works
10
user=> (eval `(~f)) ; eval calling the closure directly embedded in
the call expression -- weird exception
#<CompilerException java.lang.ExceptionInInitializerError (NO_SOURCE_FILE:40)>
user=> (def f (fn [] 10)) ; replace f with a non-closure function with
the same return value
#'user/f
user=> (eval `(~f)) ; eval calling the function directly embedded in
the call expression -- works!
10
user=>

Ken Wesson

unread,
Mar 12, 2011, 12:04:28 AM3/12/11
to clo...@googlegroups.com
On Fri, Mar 11, 2011 at 11:52 PM, Stuart Sierra
<the.stua...@gmail.com> wrote:
> `eval` invokes the Clojure compiler, which transforms data structures into
> Java bytecode.  The Clojure compiler understands Clojure data structures
> like lists, vectors, and symbols, plus a few Java types like String and
> numbers.  It doesn't know what to do with a java.util.Date.  "Can't embed
> object in code" is the compiler telling you "I don't know what to do with
> this."

Yes, but "create a static final member in the class I'm generating
bytecode for, stuff the object in that static member, and embed a
reference to the static member here" seems like a sensible thing for
it to do.

private static final Date blah = somedate;

...

something.somemethod(blah);

works in javac, but clojure's compiler hates

(eval '(.somemethod something ~somedate))

for whatever reason.

> The #= is a Clojure reader macro that means "evaluate the following code."
> The error message "maybe print-dup not defined" is suggesting that, if you
> really did mean to stick a java.util.Date in your compiled code, you have to
> provide your own implementation of print-dup.  Specifically, you need a
> method of print-dup that takes a java.util.Date and returns a data structure
> representing the Clojure code that *creates* a java.util.Date.

So, there is a workaround of sorts. A general one for serializable
objects could even be constructed: serialize to a byte array, embed
the byte array as a seq in code that turns it back into a byte array
and then deserializes an object from it. Not very efficient though.

A true fix requires changing the compiler to add static final members
to the generated class, as suggested above.

Stuart Sierra

unread,
Mar 12, 2011, 10:44:12 AM3/12/11
to clo...@googlegroups.com

Yes, but "create a static final member in the class I'm generating
bytecode for, stuff the object in that static member, and embed a
reference to the static member here" seems like a sensible thing for
it to do.


That's easy for simple types like java.util.Date, but how could it work for all arbitrary objects?  Those objects may contain pointers to other objects.  How would you stuff a java.util.TreeMap into a static field -- perform a deep copy of the entire structure?  There's no standard interface for deep copy, or even an easily-agreed-upon notion of what deep copy IS.

Java serialization runs into the same problems.  Where do you stop?  You have objects with volatile fields or pointers to system resources like filehandles.  How would you stuff a java.io.InputStream into a static field?  That's not to say you couldn't write Clojure functions to embed serialized data in 

private static final Date blah = somedate;


You can't do that, even in Java.  The constant pool in a compiled class can only contain primitives, Strings, and names.  Where does `somedate` come from?  You have to provide code to construct a java.util.Date, which javac will add to the static initializer block of the class.  Clojure does essentially the same thing with `print-dup`.

The deeper issue here is that JVM bytecode is fundamentally static: it's not allowed to contain references to objects that only exist at runtime.  In languages that have a built-in concept of a "runtime image," like Smalltalk or some Lisps, the line between static code and runtime data is blurred.

-Stuart Sierra

Ken Wesson

unread,
Mar 12, 2011, 12:34:35 PM3/12/11
to clo...@googlegroups.com

The objections you raise might be applicable when AOT compiling. They
definitely aren't otherwise, because the object that is needed
*already exists* in the same runtime instance where it will be needed.
The compiler can just create a class with a static reference pointing
at the preexisting object in that case.

The AOT compilation case is, meanwhile, the easier one to work around:

(let [x (some-complicated-object)]
(defmacro foo [args ... & body]
`(blah
(blah blah ~some-arg
(blah ~x blah blah
~@body)))))

can be rewritten as

(def x-some-suffix-that-ensures-no-collisions (some-complicated-object))

(defmacro foo [args ... & body]
`(blah
(blah blah ~some-arg
(blah x-some-suffix-that-ensures-no-collisions blah blah
~@body))))

The main case where it can be a problem is with eval rather than
macros, because whereas we can extract an extra def with macros, like
above, how would you do something like this?

(eval `(fn [x#]
(some-complex-code x#
{:time-of-evaluation ~(Date.)}
more-stuff)))

Obviously changing ~(Date.) to just (Date.) is wrong in this case,
since it's supposed to be :time-of-evaluation, not
:time-of-function-call; if ~(Date.) worked as intended the newborn
function would embed the same date in the map (the date of the
function's creation) every time it was called, whereas using (Date.)
results in the date being different every time the function is called.

At the same time we can hardly go around (re)defing the Date at
runtime. Whatever contains the eval would not be reentrant and we
wouldn't be able to have two functions floating around that it created
that have different dates. In this instance we can transform it into a
closure:

(eval `(let [d# (Date.)]
(fn [x#]
(some-complex-code x#
{:time-of-evaluation d#}
more-stuff))))

but there are other cases where it's much less trivial a
transformation. For example, the object may already exist before eval
is called and it may be something that can't be easily replaced or
recreated without repercussions, e.g. a database connection. Right
now, the *simplest* way I can think of to generate a new function
(that isn't simply a closure) at runtime that holds a preexisting
non-global database connection is

(let [foo (gensym)
ns *ns*]
(eval `(ns ~foo))
(in-ns ns)
(let [n (find-ns foo)
v (clojure.lang.Var/intern n 'db the-db-connection-we-already-have)
db-sym (symbol (str foo) "db")]
(eval `(fn [x#] ... ~db-sym ...)))

which is not only insanely ugly but in a sufficiently long-running app
will eventually blow the heap since it will create an open ended set
of Var and Namespace objects that never get garbage collected.

Of course, it's not that common to be needing eval at run-time, but
it's not unheard of either; generating compiled functions on the fly
is sometimes desired as an optimization or for distributed-computing
type purposes. And then it could be handy to allow generic objects to
be self-evaluating in non-AOT-compiled code, as it would reduce the
above horror to:

(eval `(fn [x#] ... ~the-db-connection-we-already-have ...))

!!

Reply all
Reply to author
Forward
0 new messages