Basically each lambda compiles into a method body, and the size of its
lexical environment doesn't matter very much, only the amount of code
inside the lambda's (fully macroexpanded) body. So in (fn a [x] (foo (letfn
[(b [] (bar)) (c [] (baz))])), a only "pays for" foo, and b and c pay for
bar and baz respectively. So the only cause of too-large methods is a
single large lambda that doesn't delegate to other lambdas.
I checked out midje and tried macroexpanding the test that's giving you
problems: it expands to 140KB of Clojure source! The culprit is indeed the
creation of your metadata maps: the :midje/body-source and :midje/source
keys are each around 40KB literals. I don't know the details of how quoted
values get put into the bytecode, but it's possible that code is emitted to
generate them at runtime; perhaps they go into a static initializer, but
that probably has the same code size limits anyway.
On Monday, February 4, 2013 2:53:04 PM UTC-8, Brian Marick wrote:
>
> An individual Midje "fact" (test case, roughly) macroexpands into a form
> like this:
>
> (record-fact-existence!
> (letfn [(fun1 [] ...test code is here...)
> (fun2 [] (with-meta fun1 {...metadata...}))]
> (fun2))
>
> Tabular facts in Midje are done via unification. A table with 8 rows turns
> into 8 individual facts (each of that structure), surrounded by an outer
> level fact that runs each of them. This is wasteful of space but allows
> unusually expressive tests.
>
> Something I've done recently seems to have pushed some tabular facts over
> the Java method-length limit:
>
> > Caused by: java.lang.ClassFormatError: Invalid method Code length 81209
> in class file midje/t_checkers$eval24254$this_function_here_24253__24497
>
> (This is actually surprising, since I don't see what I've done today that
> could push the size so *much* over 64K.)
>
> I hope my two breaking tabular facts are atypically large, but I fear not.
> So, some questions / thoughts:
>
> 1. Do mutually recursive `letfn` functions get compiled into a single
> method? More generally, I hope that any nested function definitions turn
> into separate methods. Do they?
>
> 2. The metadata is rather large - can it somehow end up increasing the
> method bytecodes? What if it's constructed via merging literals, like this:
>
> (clojure.core/merge
> '{:midje/body-source ((+ 1 1) => 2), ...}
> {:midje/top-level-fact? true})))]
>
> The guts of assertion checking also involves merging maps:
>
> (check-one (clojure.core/merge
> {:position
> :expected-result-form '2,
> :expected-result 2,
> :function-under-test (clojure.core/fn [] (+ 1 1))}
> {:arrow '=>, :call-form '1}
> (hash-map-duplicates-ok :position (line-number-known 2))))
>
> 3. Midje goes to a lot of trouble to obey lexical scoping, so that you can
> write, for example:
>
> (let [a 2]
> (fact
> (let [b 2]
> (+ a b) => (* a b)))
>
> Do closed-over lexical environments contribute unduly to method size?
>
> 4. How can I get a look at what a big fact compiles into? (I suppose I
> need to AOT-compile a test namespace, but I've never had much luck with
> that.)
>
> --------
> Looking for 1/2-time employment as a Clojure programmer
> Latest book: /Functional Programming for the Object-Oriented Programmer/
> https://leanpub.com/fp-oo
>
>
--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.