From: Tom Anderson on 16 May 2010 08:07 On Fri, 14 May 2010, Joshua Maurice wrote: > On May 14, 2:25�pm, Tom Anderson <t...(a)urchin.earth.li> wrote: >> On Wed, 12 May 2010, Joshua Maurice wrote: >>> //AA.java >>> public class AA { public final int x = 1; } >>> >>> //BB.java >>> public class BB { public int x = new AA().x; } >>> >>> //javap -verbose -classpath . BB >>> public BB(); >>> �Code: >>> � Stack=3, Locals=1, Args_size=1 >>> � 0: � aload_0 >>> � 1: � invokespecial � #1; //Method java/lang/Object."<init>":()V >>> � 4: � aload_0 >>> � 5: � new � � #2; //class AA >>> � 8: � dup >>> � 9: � invokespecial � #3; //Method AA."<init>":()V >>> � 12: �invokevirtual � #4; //Method java/lang/Object.getClass:()Ljava/lang/Class; >>> � 15: �pop >>> � 16: �iconst_1 >>> � 17: �putfield � � � �#5; //Field x:I >>> � 20: �return >> >>> Specifically note that the instructions to initialize BB.x involve >>> "iconst_1", which, as I understand it, puts the constant 1 on the >>> stack. javac, even with -g, inlined the value of a final not-static >>> int field. >> >> Yeah, this is a bit weird. Or not. What had thrown me was the construction of AA. Under java's rules, the value of AA.x is a compile-time constant expression, but the value of BB.x is not. This means that AA.x's value can be computed by the compiler and embedded, but BB.x's cannot. I'd thought that this meant BB.x's value would be correctly computed at runtime - but of course this is not true, because AA.x's value is a compile-time constant, so it can be inlined *in BB*. Obvious, really, but i hadn't thought it through properly. If you can impose some restrictions on the source code in your project, you can write some methods like: public class ConstantUtils { public static int constant(int x) {return x;} } And then require that constants are written like this: import static ConstantUtils.constant; public class AA { public final int x = constant(1); } Which is sufficient to stop x being a compile-time constant, thus sidestepping the whole problem. It's easy enough to audit this, too - you just look for ConstantValue attributes in the class files. I'm starting to think that a better option would be to modify javac to never inline compile-time constants outside their defining class. >> I didn't know about ghost dependencies, so i didn't deal with those at >> all. But on that subject - am i right in thinking that to build the set of >> ghost dependencies, you need to know every name used by the class? If so, >> doesn't that already cover this situation? CC uses the name BB.x, and >> presumably you have to have an inheritance rule like the above that means >> that a change to AA.x means a change to BB.x if there is no actual BB.x. > > See > http://www.jot.fm/issues/issue_2004_12/article4.pdf > for "ghost dependencies". > > I don't think as presented in the paper that ghost dependencies will > catch this. Again, take the example > //AA.java > public class AA { public final int x = 1; } > //BB.java > public class BB extends AA {} > //CC.java > public class CC { public final int x = new BB().x; } > > CC.java has ghost dependencies "CC", "BB", "x", aka all names in the > class file Firstly, i don't think there's a dependency on 'x' - there is no use of the unqualified name x in CC. Consider that a dependency on 'x' implies that adding a new entity named x would affect compilation, but if i added a new top-level class called x, or a nested class in CC called x, or imported a type called x into CC, then that wouldn't affect the interpretation of "new BB().x". Lagorio's paper is rather short on detail on how name are found - he glosses over it as trivial. I get the impression he only considers what you might call 'unrooted' names, though - ones without a dot to the left. Secondly, the way Lagorio defines his 'requiring' relation involves a transitive closure - this is easy to miss, but it's rather significant. In this case, it means that if you changed AA, you would recompile CC, so the change to AA.x would be caught. But in the general case, it means you have to recompile huge numbers of unrelated sources after a change which only has limited effects. As you say: > I'm not sure offhand if there is a good way to extend ghost dependencies > to catch this case without introduces a lot of false positives. I'm not clear if you're proposing to use Lagorio's whole scheme, or just the notion of ghost dependencies. If the latter, how are you going to combine it with the traditional approach? > I've also given some thought as you had to maintain this list keeping > track of super classes. I'm not sure how it would interact with this > example: > > //AAA.java > public class AAA { public static int aaa = 1; } > //BBB.java > public class BBB { public static AAA bbb = null; } > //CCC.java > public class CCC { public static BBB ccc = null; } > //DDD.java > public class DDD { public final int ddd = CCC.ccc.bbb.aaa; } > > If we chance AAA.aaa to "public static double aaa = 2", then BBB.class > would be a noop recompile, CCC.class would be a noop recompile, but > DDD.class would need a recompile. Under Lagorio's rules, a change to AAA would mean recompiling all these classes, because they transitively depend on AAA. Under my rules, it would mean recompiling BBB and DDD, because they both depend directly on AAA. You could refine my scheme to distinguish between a dependency on a type merely existing and on the details of its signature, which would let you skip the recompilation of BBB here, but i don't know how worthwhile that would be in practice. I'm not sure what this has got to do with superclasses, though. > Again, I think I would need the same information to make this work > without endless cascading; I would need to know that DDD (directly) uses > AAA. I thus think that your / my scheme of keeping tracking of super > classes would not be terribly effective / productive. Again, that example has nothing to do with superclasses. What are you thinking of here? tom -- There was this one time we were talking about [...] that if you fill a bucket with water and spin it around, some of the force acting to hold the water back comes from the edge of the Universe. We were on tour in the Netherlands, and when we got to the venue, backstage someone said, 'let's try it out'. So we yelled, 'Oi, bring us a bucket and some rope!' They did and we made an awful mess. -- Billy Bragg, feat. Ernst Mach
From: Tom Anderson on 16 May 2010 08:32 On Sun, 16 May 2010, Tom Anderson wrote: > I'm starting to think that a better option would be to modify javac to > never inline compile-time constants outside their defining class. Which of course you can't do, because you can use them in forming case labels in switch statements, and those have to be inlined. Balls! tom -- I am the best at what i do.
From: Tom Anderson on 16 May 2010 10:37 On Sun, 16 May 2010, Lew wrote: > Tom Anderson wrote: >>> I'm starting to think that a better option would be to modify javac to >>> never inline compile-time constants outside their defining class. >> >> Which of course you can't do, because you can use them in forming case >> labels in switch statements, and those have to be inlined. Balls! > > And, of course, you don't want to do anyway because it defeats that > elementary optimization. I couldn't give two figs about any optimisation javac does. The ones the VM does are the only ones that matter. I would be very disappointed indeed if the VM failed to make this optimisation at runtime without javac's help. > Defeating inbuilt optimizations of your code because you don't want to > correctly design or refactor your code is foolishness. Oh please. Lew, this is absurd. Do you use Eclipse, or some other IDE with an incremental compiler? If so, would you rather switch to an IDE that did a full build every time you changed any file? No? Then you are *already* agreeing through your very actions that incremental compilation is useful. This discussion is merely about ways to do it more simply, without having to build, as Eclipse does, a vast database of cross-references. tom -- As a matter of fact, it is estimated that 10% of all meth labs explode.
From: Lew on 16 May 2010 11:01 Lew wrote: >> Defeating inbuilt optimizations of your code because you don't want to >> correctly design or refactor your code is foolishness. Tom Anderson wrote: > Oh please. Lew, this is absurd. Do you use Eclipse, or some other IDE > with an incremental compiler? If so, would you rather switch to an IDE > that did a full build every time you changed any file? No? Then you are > *already* agreeing through your very actions that incremental > compilation is useful. This discussion is merely about ways to do it > more simply, without having to build, as Eclipse does, a vast database > of cross-references. I never said incremental compilation is useless. Straw man. I said getting rid of compile-time constants to get it is foolish. -- Lew
From: Lew on 16 May 2010 11:03
On 05/16/2010 11:01 AM, Lew wrote: > Lew wrote: >>> Defeating inbuilt optimizations of your code because you don't want to >>> correctly design or refactor your code is foolishness. > > Tom Anderson wrote: >> Oh please. Lew, this is absurd. Do you use Eclipse, or some other IDE >> with an incremental compiler? If so, would you rather switch to an IDE >> that did a full build every time you changed any file? No? Then you are >> *already* agreeing through your very actions that incremental >> compilation is useful. This discussion is merely about ways to do it >> more simply, without having to build, as Eclipse does, a vast database >> of cross-references. > > I never said incremental compilation is useless. Straw man. > > I said getting rid of compile-time constants to get it is foolish. Actually, what I said was getting rid of compile-time constants instead of creating a good design in order to get incremental compilation is foolish. -- Lew |