Incremental Java Compile

J

Joshua Maurice

Strictly speaking, it doesn't.  It's a really, really, really good
idea to study the language spec if you actually want to learn Java.
The relevant section is:
<http://java.sun.com/docs/books/jls/third_edition/html/
typesValues.html#10931>

I was being facetious. Oh nevermind. This little train of conversation
is going nowhere.

tl;dr I mentioned debug information in class files combined with ghost
dependencies might be enough. Later, when I found out it wasn't
enough, I posted here correcting myself saying "Actually, it's not".
Arne Vajhøj said "We told you". I said "No you didn't". He said "Yes
we did". And that is about the extent of the short thread of
conversation.
 
A

Arne Vajhøj

Ok. Let me try again. I recall only discussions of static finals. The
problem of expanding inline "things" also happens with non-static
final fields. I do not recall any discussion previous to that post of
mine about non-static final fields being a problem.

True.

It was static final we discussed.

But given that something is final the difference between
static and non-static is pretty small.

Arne
 
J

Joshua Maurice

True.

It was static final we discussed.

But given that something is final the difference between
static and non-static is pretty small.

Apparently. I'm sorry. I did not know. I was definitely under the
impression that specifically "static final" primitives can be expanded
inline. I thought there was a special language allowance for
specifically "static final". Apparently it's for "final", static or
not, (or Sun's javac is broken).
 
L

Lew

someaccess static final sometype somename = somevalue;

is Java's implementation of what is generally known as
a constant.

The point is that the JLS defines what a constant is, and "static" is not
necessary for the definition, nor is "final" sufficient.

Some say that it used to be "generally known" that the Earth is flat, but that
didn't make it so.

In the Java world, the term "constant" (actually, "constant variable") has a
precise definition. It ought to be be "generally known" to be what it
actually is, and to promulgate a different conception is a disservice to those
seeking to achieve proficiency in Java programming.
 
L

Lew

Joshua said:
I was definitely under the
impression that specifically "static final" primitives can be expanded
inline. I thought there was a special language allowance for
specifically "static final". Apparently it's for "final", static or
not, (or Sun's javac is broken).

Again, the JLS defines the term "constant variable" precisely, and that
definition does not rest on staticness, nor stop on finality. Please read the
link referenced upthread for the definition.
 
L

Lew

Joshua said:
... code i [sic] compile too (javac 1.6.0_16).

You really should upgrade your Java version. Minor version numbers (it's up
to _20 now) represent bug and security fixes, not the sort of thing you should
skip. In particular, _20 was released to resolve a security hole that got a
lot of publicity a couple of weeks ago.
 
A

Arne Vajhøj

The point is that the JLS defines what a constant is, and "static" is
not necessary for the definition, nor is "final" sufficient.

Some say that it used to be "generally known" that the Earth is flat,
but that didn't make it so.

In the Java world, the term "constant" (actually, "constant variable")
has a precise definition. It ought to be be "generally known" to be what
it actually is, and to promulgate a different conception is a disservice
to those seeking to achieve proficiency in Java programming.

No.

It is important for any Java programmer to understand general IT
language as well.

And the flat earth analogi is phony, because that was not correct
while the general usage of constant is correct.

Arne
 
L

Lew

Lew said:
Yes.

It is important for any Java programmer to understand general IT
language as well.

And the flat earth analogi is phony, because that was not correct
while the general usage of constant is correct.

Java uses many terms in a language-specific way, and "constant variable" is
one of them, also the one relevant to the OP. He was interested in whether a
class reference is recoverable from another class, and for that the Java
definition is what's relevant, not the "general usage".

Also, also, you were saying that "static final" is equivalent to "constant".
That is not "general usage". You can't just shift ground now simply to try to
be right. That's the "Not a real Scotsman" fallacy. It simply is not true
that a 'static final' variable is a constant variable in Java.

You are doing would-be Java programmers a *huge* disservice by misdefining the
term. You are being silly trying to change the terms of discourse to avoid
the correct definition.
 
T

Tom Anderson

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.
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
 
T

Tom Anderson

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
 
T

Tom Anderson

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
 
L

Lew

Tom said:
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.
 
L

Lew

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.
 
L

Lew

Tom said:
Oh please. Lew, this is absurd. Do you use Eclipse, or some other IDE

Not by preference, but that has nothing whatsoever in the least at all even a
jot to do with my point, which you misrepresented.
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 use Eclipse when I have to. Its incremental compilation is not appreciably
better for me than Ant's, which is plenty good enough for a well-designed
project, which excludes the OP's.
 
M

Mike Schilling

Tom said:
I'm starting to think that a better option would be to modify javac to
never inline compile-time constants outside their defining class.

Better to modify javac to make constant table entries for these compile-time
constants while still inlining them. These constant table entries would
never be used, but they'd document the dependency.
 
T

Tom Anderson

I never said incremental compilation is useless. Straw man.
Okay.

I said getting rid of compile-time constants to get it is foolish.

I'm saying there is *no* downside to getting rid of compile-time
constants (where that's possible!).

tom
 
T

Tom Anderson

Better to modify javac to make constant table entries for these
compile-time constants while still inlining them. These constant table
entries would never be used, but they'd document the dependency.

That's what i had been thinking previously. That information would let you
do the dependency tracking that would let you recompile the right classes.
But stopping the inlining in the first place would mean that there was no
need to do the recompilation at all. If constants weren't inlined, there'd
be no need to consider constant values part of a class's signature, and so
changes to a class which only changed constant values would not need to
trigger any other recompilation. When classes did change more
significantly, the set of other classes which needed recompilation would
be smaller. That seems like a bigger win.

Of course, the switch label problem defeats this idea. If you're inlining
some constants, then you still need to track dependencies on constant
values in general.

tom
 
J

Joshua Cranmer

Also, what the hell is that getClass call all about? I see that in code
i compile too (javac 1.6.0_16). A bit of googling reveals it's the code
generated to force a null check of a variable, and this is used in
compiling certain contortions involving inner classes. But there's no
inner class here, and there is no way in a month of sundays that the top
of stack can be null at instruction 12 - it's produced by applying dup
to the result of new, and new can never produce a null (right?). So
what's it doing?

I'm guessing that what happens is this:
new BB().x gets converted into an AST which is roughly a FieldAccess
where the object is (new BB()) (an opaque expression) and the field is
"x". The code generation sees an opaque expression--and expressions may
be null, so it does the check. The key is that it doesn't know that the
expression is of a type which cannot return null--Java does not do that
much static analysis at compile time, to my knowledge.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top