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 clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com 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 clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.