Lifting for Primitive and Boxed Types and Strings

When we examined the code that was generated for the sparse matrix multiplication benchmark of Mint, we noticed that variables containing loop indices had become cross-stage persistent (CSP) variables, which necessitated an array lookup. This was much more expensive than directly accessing a final local variable.

I have now implemented lifting for primitive and boxed types as well as for strings. For example, the code

final int i = 1;
Code c = <| i |>;

used to generate code that looked like this:

public class $$Code1$$ implements edu.rice.cs.mint.runtime.Code {
private Object [] $csp_table;
public $$Code1$$ (Object [] csp_table) { this.$csp_table = csp_table; }
public Integer run() {
return $csp_table[0];
}
}

Even though the value of i was final, we retrieved the value from the CSP table. Now we lift this value, i.e. we create a code object for it and escape it into the bracket using a edu.rice.cs.mint.runtime.Lift.liftint helper method:

public class Lift {
Code liftint(int value) { ... }
}

The compiler internally transforms

Code c = <| i |>;

into an escape-and-lift that looks like

Code c = <| `(edu.rice.cs.mint.runtime.Lift.liftint(i)) |>;

Now the code that is generated looks as expected:

public class $$Code1$$ implements edu.rice.cs.mint.runtime.Code {
private Object [] $csp_table;
public $$Code1$$ (Object [] csp_table) { this.$csp_table = csp_table; }
public Integer run() {
return 1;
}
}

This has lead to nice performance improvements in the matrix multiplication and loop unrolling benchmarks, and (perhaps more spectacularly) also in the Lint interpreter benchmarks. The staged Lint interpreter benchmarks now run 20 times as fast as the unstaged versions. Matrix multiplication shows a speedup of a factor of 4.8, and loop unrolling has a speedup of 2.3.

There is a new release of the Mint implementation (November 4, 2009) and a new version of DrJava with Mint (drjava-r5128-mint-r14460).

(Re-posted from The Java Mint Blog)

Share
Posted in Mint | Leave a comment

Print This Post Print This Post  

DrJava Interactions Pane Output Redirection

We got a question today about how the output of a Java program running in DrJava‘s Interactions pane could be redirected into a file, just like it could be done in a Unix or DOS shell:

java Motion > motion.dat

The Interactions pane supports a simulated java command, but this is merely for convenience. java Motion actually translates to Motion.main(String[] {}), which is a bit unsightly. The Interactions pane is not a Unix or DOS shell, and therefore doesn’t support input/output redirection, nor does the java command support any of the command line options.

However, there is still a way to do input/output redirection in the Interactions pane. Here are instructions on how to redirect the System.out stream:

  1. Compile your program (e.g. Motion.java).
  2. In the Interactions pane, import the classes from the java.io package:
    import java.io.*
  3. Store the old System.out stream in a variable:
    PrintStream oldOut = System.out
  4. Create a new PrintStream that writes to a file:
    PrintStream newOut = new PrintStream(new FileStream("motion.dat"))
  5. Set the new stream as System.out:
    System.setOut(newOut)
  6. Now run your program (but not using the “Run” toolbar button or menu item):
    java Motion
  7. Your program will run, but all output from System.out is written to
    the motion.dat file.
  8. To get the original behavior of System.out back, set the old stream again:
    System.setOut(oldOut)

Perhaps we could support > and < to do this kind of setup automatically (although only when the java or applet commands are used, to avoid ambiguity). There is a feature request for that already: 1506832: Interactions commands.

Share
Posted in DrJava | Leave a comment

Print This Post Print This Post  

Mint Matrix Market Reader for Sparse Matrix Multiply Benchmark

I added a Matrix Market file reader for the MatrixMultiplySparse benchmark and used the 183-by-138 matrix we had picked out.

The reader seems to work, and if I set the heap size to 1.3 GB, then the compiler almost gets through, but it then blows up in Code.checkLimits. I believe the we are hitting the code size limit of 64 kb per method:

The amount of code per non-native, non-abstract method is limited to 65536 bytes by the sizes of the indices in the exception_table of the Code attribute (§4.7.3), in the LineNumberTable attribute (§4.7.8), and in the LocalVariableTable attribute (§4.7.9).

I’m going to try smaller matrices.

Share
Posted in Mint | Leave a comment

Print This Post Print This Post  

Funny Feature Request of the Day

Funny feature request of the day: Zoom In Zoom Out

I would like to request you to please kindly add Zoom in Zoom out feature in print preview. Please do it as soon as possible bcz we are having difficulties to view the code clearly as we r having eye problem.

(Emphasis mine.)

I’m afraid this has rather low priority…

Share
Posted in DrJava, Ramblings | Leave a comment

Print This Post Print This Post  

Mint Compile Time Improvement

I just committed a small change as a result of profiling Mint programs: We are now treating all compiler invocations as one long compile session, instead of having many short ones during the execution of a Mint program. This reduces overhead since each symbol only needs to be completed once.

Compile times are cut by a factor of about 3 in our benchmarks. Not as much as I’d hoped, but it’s a start. It seems like my initial estimates were skewed by the presence of the profiler itself.

I’m going to profile again now.

Update:

I got another improvement by a factor of about 1.5 by replacing an O(n) list append with an O(1) list prepend. But I think now it will be difficult to get anything more without major rewrites of the compiler.

Compiling now represents 11% of a profiled run of LintFib, parsing is 1% (keep in mind that profiling skews the percentages).

Share
Posted in Mint | Leave a comment

Print This Post Print This Post  

Legitimate Chase Email?

I received an email from Chase, and it seems to be legitimate, but gmail flags it as suspicious. No wonder, it contains the following link:

www.Chase.com

The domain cardmemberservices.com belongs to JPMorgan Chase as well, but it is not the same domain as visible, www.Chase.com.

I don’t click on links that take me to important account websites at all, but this bothers me nonetheless. Why does Chase do this? They should make their emails simple to vet.

Share
Posted in Uncategorized | Leave a comment

Print This Post Print This Post  

Softpedia “100% Clean” Award for DrJava

DrJava has been included in the Softpedia software database. Furthermore, their staff certifies with their “100% Clean” award that our software is free from malware:

DrJava has been tested in the Softpedia labs using several industry-leading security solutions and found to be completely clean of adware/spyware components. We are impressed with the quality of your
product and encourage you to keep these high standards in the future.

To assure our visitors that DrJava is clean, we have granted it with the “100% CLEAN” Softpedia award. To let your users know about this certification, you may display this award on your website, on software boxes or inside your product.

Of course I’m not surprised by this, but perhaps it may actually reassure some users. There is all kinds of crappy, evil software out there.

Softpedia 100% Clean Award

Softpedia 100% Clean Award

Share
Posted in DrJava | Leave a comment

Print This Post Print This Post  

Paper: Test-First Java Concurrency for the Classroom

Test-First Java Concurrency for the Classroom

41st Technical Symposium on Computer Science Education (SIGCSE 2010)

Concurrent programming is becoming more important due to the growing dominance of multi-core processors and the prevalence of graphical user interfaces (GUIs). To prepare students for the concurrent future, instructors have begun to address concurrency earlier in their curricula. Unfortunately, test-driven development, which enables students and practitioners to quickly develop reliable single-threaded programs, is not as effective in the domain of concurrent programming. This paper describes how ConcJUnit can simplify the task of writing unit tests for multi-threaded programs, and provides examples that can be used to introduce students to concurrent programming.

Share
Posted in Concurrent Unit Testing, Publications | Leave a comment

Print This Post Print This Post  

SIGCSE Paper Accepted!

The paper that I submitted to SIGCSE a month and a half ago, “Test-First Java Concurrency for the Classroom”, got accepted!

That means I’m going to Milwaukee in March.

Update:

I just realized that if my other paper currently under review also gets accepted, it will get a little stressful:

March 9-12, 2010: CSEE&T in Pittsburgh
March 10-13, 2010: SIGCSE in Milwaukee

Share
Posted in Concurrent Unit Testing, Research | Leave a comment

Print This Post Print This Post  

A Repeating Pattern of Hate: SourceForge

I’m hating SourceForge again, and this is a very frequent thing.

This time, I’m hating it for two reasons:

  1. News submission is broken. I made a new DrJava development release, drjava-20091021-r5004, but I can’t post a news release, because the submission website just goes nowhere. I filed a ticket, but as usual, SourceForge just closes it. How about keeping it open until you fix it?
  2. The task manager is gone. Just gone! Apparently there’s a replacement, but I can’t find it, and it’s not documented anywhere. (@ Update: Apparently, because TaskFreak is a hosted app, it’s not documented in the same place, but here. @)

I realize SourceForge is providing a free service to us, but do you have to make it such a huge pain? I honestly don’t care about how nice and glossy the website looks, I want to be able to use it.

Share
Posted in DrJava, Ramblings | Leave a comment

Print This Post Print This Post  

New ConcJUnit Annotations and Method Name Suffixes

I guess I should mention that I changed the ConcJUnit annotations (@Test) and method name suffixes (testFoo_NOJOIN) a little bit.

I had noticed that putting a _NOJOIN suffix on a test method or using a @Test(forceThreadJoin=false) annotation entirely disabled the test thread group. That’s not really what I wanted to happen, and even if it had been, it was badly named.

Instead of having this conflated behavior, I now have three different flags that can be set. The @Test annotation member has also been renamed.

_NOTHREADS or @Test(checkThreads=false)
Disables the test thread group; now ConcJUnit behaves like JUnit and ignores failures in threads other than the main thread.
_NOJOIN or @Test(checkJoin=false)
Disables the check if threads are still alive.
_NOLUCKY or @Test(checkLucky=false)
Disables the check if threads ended, but only due to lucky circumstances.

Setting checkThreads=false also disables the join and lucky checks; setting checkJoin=false also disables the lucky checks. These changes have been made in the 20091020 release.

I should build Javadocs and include them in DrJava as well. The latest development release has the ConcJUnit 20091020 library that corresponds to JUnit 4.7, but the included API Javadoc is still JUnit 4.4. That is kind of messed up. (Update: Done.)

Share
Posted in Uncategorized | 2 Comments

Print This Post Print This Post  

New Version of DrJava with Mint

I just made a new release of the all-in-one DrJava with Mint:

drjava-r5121-mint-r14186.jar

On the Mint side, there is only one small change: Previously, the Mint compiler only appeared in the “Compiler” drop-down box if the “Display all compiler versions” preference was enabled, which by default was not the case.

Happy experimentation with Mint!

(Re-posted from The Java Mint Blog)

Share
Posted in Mint | Leave a comment

Print This Post Print This Post  

Today’s Loose Ends

I’m in the process of wrapping up today’syesterday’s loose ends:

And now it’s officially tomorrowtoday.

Share
Posted in Concurrent Unit Testing, Mint, Research | Leave a comment

Print This Post Print This Post  

Two Confusing Errors

A while ago, I was told by our one-and-only client of Mint that the version of DrJava with Mint does not display Mint as a compiler, even though it is included in the jar file and therefore should be available. I couldn’t reproduce this, so I set it aside for a while.

Yesterday I realized that there was a logic problem with the “show all compiler versions” preference in DrJava. By default, it is disabled, and that means that DrJava will only show the “best” compiler of each major Java version: Only one Java 6 compiler is shown, and in our notion of “best”, all Java 6 compilers (Sun, Apple, OpenJDK) are “better” than Mint. This was done to avoid using Mint as the default compiler. As a result, if “show all compiler versions” is disabled, Mint is never shown.

I rewrote the logic for this preference to treat compilers from different versions separately, but in doing so, I ran into a very confusing problem: My changes to JavaVersion.FullVersion didn’t seem to “stick”. I’d make changes to the plt.jar library, compile it, copy the plt.jar file into DrJava’s lib directory, rebuild DrJava, but still get the old behavior. I eventually resorted to writing the code in a much more complicated way that didn’t involve changing plt.jar.

On my bike commute to work today I realized what the problem was: The ConcJUnit jar that I started linking with DrJava a while ago included its own version of JavaVersion.FullVersion, and I wasn’t rebuilding ConcJUnit! So what I needed to do is create a ConcJUnit that doesn’t include dependencies like the plt library. One of our other DrJava developers, Dan Smith, had of course long realized that and included such a “jar-base” target for his libraries… Smart!

Because of the explosion of ConcJUnit distributables, I have decided to only make the most important ones available. Right now, that is a version corresponding to JUnit 3.8.2, and one corresponding to JUnit 4.7. The 4.x versions in between won’t be released by default anymore.

The files released are:

concjunitrt.jar
The processor for the runtime environment (necessary to emit “lucky” warnings).
concutest-junit-version-src.jar
The source code.
concutest-junit-version.jar
The stand-alone binary, without the runtime processor (concjunitrt.jar), but with all required dependencies.
concutest-junit-version-withrt.jar
The stand-alone binary with the runtime processor and all required binaries.
concutest-junit-version-withrt-nodep.jar
The binary with the runtime processor but without dependencies (namely the plt.jar file).

I’ll make a new ConcJUnit release soon, and also create a new DrJava with Mint jar file.

Share
Posted in Concurrent Unit Testing, DrJava, Mint, Ramblings | Leave a comment

Print This Post Print This Post  

More Artificially Complex Problem Spaces

At the Rice Computer Science Affiliates Meeting, I just listened to an interesting talk by Keith Cooper about the PACE (platform-aware compilation environment) project.

Keith mainly talked about resource and configuration detection, e.g. detecting cache sizes and alignment. Moshe Vardi commented that it was “a high-tech solution to a low-tech problem” that could be solved with some configuration files. Keith replied that his group didn’t write the rules of the game, and that they had to conform to the grant requirements.

This seems somewhat similar to the comment I made in my last post, that I was, in my work, exploring an artificially complex problem space instead of finding something that is intrinsically true. The result of my work is often interesting hacking, but the value of it is dependent on the longevity and ubiquity of the platform.

Update

Keith contacted me to point out that I have misunderstood the problem. The goal of this stage of the PACE project is not merely to detect hardware properties, like the number of registers, but to determine values that impact program performance, like the number of live values a procedure can use before performance degrades. Those values are dependent both on hardware and software (e.g. compiler and operating system).

Share
Posted in Ramblings | Leave a comment

Print This Post Print This Post  

AWT Exception Handler Cannot Be Reset After Exception

When testing ConcJUnit some more from inside DrJava, I noticed some weird behavior. I had a JUnit 3.8.2 version of a test, and an equivalent JUnit 4 version. Run independently, they behaved as expected (which in my line of work usually means the tests failed), but if I ran them in sequence, then the second test didn’t catch certain errors.

After some experimentation, I found out that it was a problem with the AWT exception handler. The AWT EventDispatchThread class caches the name of the AWT exception handler class the first time an uncaught exception occurs in the event thread; therefore, even when the sun.awt.exception.handler Java property is changed, the same handler will continue to be invoked. Here’s the code from EventDispatchThread:

private static final String handlerPropName = "sun.awt.exception.handler";
private static String handlerClassName = null;
private static String NO_HANDLER = new String();
// ...
private boolean handleException(Throwable thrown) {

try {

if (handlerClassName == NO_HANDLER) {
return false; /* Already tried, and failed */
}

/* Look up the class name */
if (handlerClassName == null) {
handlerClassName = ((String) AccessController.doPrivileged(
new GetPropertyAction(handlerPropName)));
if (handlerClassName == null) {
handlerClassName = NO_HANDLER; /* Do not try this again */
return false;
}
}
// ...

ConcJUnit used different handler classes for JUnit 3-style and 4-style tests, and if a JUnit 3-style test first caused an uncaught exception, a subsequent JUnit 4-style test would not get notified of uncaught exceptions, and vice versa. Now I’m just using one handler, and it “works”.

This still has further implications, though:

  1. If the sun.awt.exception.handler uncaught exception handler property is set and an uncaught exception happens before ConcJUnit can set it, then uncaught exceptions may be missed.
  2. Code that expects to be able to set the sun.awt.exception.handler property cannot be tested correctly with ConcJUnit, because ConcJUnit relies on setting it first.

There are ways around these issues. For example, to circumvent the second problem, ConcJUnit can set the property and immediately cause an uncaught exception in the event thread itself. That way, its own handler is sure to be installed. This handler could then continue to monitor the sun.awt.exception.handler property, and if it changes, delegate to that handler instead of recording an uncaught exception. The first problem isn’t really one if ConcJUnit is used properly.

I won’t implement these improvements, though, unless I notice they are necessary.

This caching behavior, however, showed me two things again: First, it feels like I’m often exploring an artificially complicated problem (Java) space in my research, not something that is intrinsically true. Second, this really is a hack (and Sun says so). I know why Sun implemented the caching, namely to minimize reflection costs, but I don’t think it was done well. Sun really should have cached the class and the class name, and when the class name differs from the Java property, then the class lookup using reflection should have been re-done.

I don’t think this caching behavior is documented anywhere except in the source.

Share
Posted in Concurrent Unit Testing, Ramblings, Uncategorized | Leave a comment

Print This Post Print This Post  

And the Annoying Twit of the Month Award Goes to…

And the annoying twit of the month award goes to… this guy, a student at the Ozarks Technical Community College, for his truly legendary effort of generating 38 support emails to us without considering that the problem might be in his own code.

Over the course of just two days, he managed to open five bug reports, the last one, World revolving around me. . ., definitely out of spite. It even involved a thinly veiled threat of badmouthing our product, DrJava. And to be even more annoying, he kept resetting our priority and resolution fields on the tracker to super urgent. And I even installed Windows Vista for him!

And what was the problem? No incompatibility with Vista, no bug in DrJava. When he finally sent his files (on Chris Warrington’s request — thanks), it turned out he was using a modified version of the Georgia Tech multimedia library, and those (probably self-made) modifications happened to cause an infinite recursion. Is anyone surprised he always blew the stack?

He was creating an infinite recursion in his own program. He could have just look at the stack trace:

...
at SimplePicture.getPixel(SimplePicture.java:323)
at SimplePicture.setAllPixelsToAColor(SimplePicture.java:189)
at SimplePicture.(SimplePicture.java:88)
at SimplePicture.(SimplePicture.java:60)
at Pixel.(Pixel.java:36)
at SimplePicture.getPixel(SimplePicture.java:323)
...

In SimplePicture.getPixel method, line 323, he is calling the Pixel constructor. Pixel extends SimplePicture, so when Pixel‘s constructor (line 36) gets called, it first calls the constructor of the superclass, namely the SimplePicture constructor.

The SimplePicture constructor in line 60 calls first delegates to another SimplePicture constructor, and that second SimplePicture constructor calls the setAllPixelsToAColor method in line 88.

Finally, in line 189, setAllPixelsToAColor calls SimplePicture.getPixel, and the whole thing starts over again.

Hopefully he will not file another bug report or discuss this issue any further on our bug tracker. Instead, he should refer his professor to this analysis of his own programming bug and let the professor help him understand his own mistake.

Closed for good. Hopefully.

Share
Posted in DrJava, Ramblings | 2 Comments

Print This Post Print This Post  

Confusing Hudson Test Failure

After moving our Hudson server to a new server, we started to experience a very confusing test failure in the Java Language Levels module.

The test that failed was AdvancedLevelTest.testShouldBeErrors. It compiles the files in javalanglevels/testFiles/forAdvancedLevelTest/shouldBreak/ and makes sure that they all convert with errors.

I had tried to figure out what causes this, since we haven’t changed the code base, but I failed to see the connection. Here is a summary of the facts:

  • The test failed on denmark.cs on the local partition.
  • The test passed on denmark.cs on the network file system.
  • The test passed on finland.cs on the network file system.
  • The test passed on finland.cs on the /local partition.

From all I can tell, the software (JDK, Ant, JavaCC, etc.) we are using on denmark.cs and finland.cs is the same. It definitely is the same when I switch on denmark.cs from /local to ~.

In the end, it turned out to be a badly written test. It didn’t create a new LanguageLevelConverter for each file to be converted, so the (expected) errors from a previous conversion counted towards the errors of following conversions. There were three files that converted without errors.

For some reason, the order in which the files in javalanglevels/testFiles/forAdvancedLevelTest/shouldBreak/ are enumerated and compiled differs on denmark’s local file system. On denmark’s local file system, one of those files that converts without error is processed first, letting the test fail. On other systems, the three tests aren’t processed until later, and the test succeeds even though it shouldn’t.

I fixed the test, but I don’t know the JLL well enough to decide if this is just a problem with the test, or if there is something wrong with the converter.

This was a very confusing, almost mysterious test failure. It was made so hard to figure out by the combination of a badly written test and the unexpected difference in file enumeration order. The latter really should not matter, but the flaw in the test case made it significant.

There’s still no version 1.328 at this time that fixes the mail configuration bug (issues #4586 and #4596), but other than that, Hudson seems to be fully operational on denmark.cs now.

Update

Hudson 1.328 is available now, and I have already updated.

Share
Posted in DrJava, Ramblings | Leave a comment

Print This Post Print This Post  

DrJava’s Most Annoying Bug is Sun’s JVM Bug

Corky just notified me that the very pesky ArrayIndexOutofBoundsException in DrJava that has been reported in numerous bug reports (assigned to be duplicates of the first report, 2831821: Caret updating is broken) is actually a JVM bug, not a bug in DrJava:

This is a documented bug in Java 6 which is supposed be fixed very soon in a new JVM release. (Update 16 build 2 is supposed to fix it but Update 16 build 1 is the current JDK release.)

See http://bugs.sun.com/view_bug.do?bug_id=6828938 and http://bugs.sun.com/view_bug.do?bug_id=6857057.

In my experience, it does not adversely affect the behavior of Drjava so I ignore it. It happens often on Linux with the Plastic look-and-feel.

The exception trace we have been seeing had no DrJava code in it:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 8
	at sun.font.FontDesignMetrics.charsWidth(FontDesignMetrics.java:492)
	at javax.swing.text.Utilities.getTabbedTextOffset(Utilities.java:381)
	at javax.swing.text.Utilities.getTabbedTextOffset(Utilities.java:302)
	at javax.swing.text.Utilities.getTabbedTextOffset(Utilities.java:286)
	at javax.swing.text.PlainView.viewToModel(PlainView.java:403)
	at javax.swing.text.FieldView.viewToModel(FieldView.java:263)
	at javax.swing.plaf.basic.BasicTextUI$RootView.viewToModel(BasicTextUI.java:1540)
	at javax.swing.plaf.basic.BasicTextUI.viewToModel(BasicTextUI.java:1089)
	at javax.swing.text.DefaultCaret.moveCaret(DefaultCaret.java:311)
	at javax.swing.text.DefaultCaret.mouseDragged(DefaultCaret.java:565)
	at java.awt.AWTEventMulticaster.mouseDragged(AWTEventMulticaster.java:303)
	at java.awt.Component.processMouseMotionEvent(Component.java:6285)
	at javax.swing.JComponent.processMouseMotionEvent(JComponent.java:3285)
	at java.awt.Component.processEvent(Component.java:6006)
	at java.awt.Container.processEvent(Container.java:2041)
	at java.awt.Component.dispatchEventImpl(Component.java:4604)
	at java.awt.Container.dispatchEventImpl(Container.java:2099)
	at java.awt.Component.dispatchEvent(Component.java:4434)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4574)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4255)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4168)
	at java.awt.Container.dispatchEventImpl(Container.java:2085)
	at java.awt.Window.dispatchEventImpl(Window.java:2475)
	at java.awt.Component.dispatchEvent(Component.java:4434)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

The bug description says:

The test GlyphView2021 mis-uses the parameters `pos’ and `len’ to the GlyphView.getBreakWeight method. These parameters are expected to be in font-related units (pixels or whatever), but in the test they are passed as logical offsets into document (i.e., a number of characters). So in 2-nd run an interval passed to getBreakWeight is 12, and the width of two characters is 14. So they don’t fit and there’s no breakpoint in the region.

What a relief, I was starting to get frustrated with this. I hope the next JVM update is released soon.

Share
Posted in DrJava | 1 Comment

Print This Post Print This Post  

String Comparisons for Common Words Are Bad

It has been a long time since the last item went on the code pranger. I’ve pulled my hair out over many things during the last months, but this is the first time in a long while that I slapped my forehead and wanted to run away.

The issue was first discovered by one of our users and submitted as a bug report. Let me first emphasize that this is clearly a bug in our DrJava code base, not in the submitted sample:

public class CompilerErrors {
int shared = 5;
public void sampleMethod() {
int x = 20;
System.out.println("This is sampleMethod. x is " + x);
}

Note in particular that the closing brace is missing, so we would expect something like “} expected” or “reached end of file while parsing”. The user noticed, however, that when he compiled the following file with Java 6, he got an internal DrJava error instead.

The problem is that we are using String messages to pass the compiler errors and exceptions thrown in the compiler, and then we try to separate these two categories again: Compiler errors are displayed in the “Compiler Output” pane, exceptions are reported as an error in the “DrJava Errors” window.

How did we decide to distinguish errors and exceptions? Using a very unstructured string comparison:

if (message != null && (message.indexOf("CompilerError") >= 0)) throw new UnexpectedException(message);

If the word “CompilerError” occurs in the error message, it is treated as exception. Since the position of the “CompilerError” substring isn’t restricted, any silly compiler error in a file named CompilerError.java, or CompilerErrorModel.java, or CompilerErrorModelTest.java, or CompilerErrorPanel.java — all of which exist in the DrJava code base! — will be treated as serious unexpected exception in the compiler.

It is a really bad idea to use common words that could occur in user-generated text, like file names, as a marker for error conditions.

The solution for this problem, of course, was to analyze the structure of the error messages more closely: The class name of the exception, fully qualified as sun.tools.java.CompilerError had to appear as part of the string "Compile exception: sun.tools.java.CompilerError" at the beginning of the message. Spaces are not allowed in Java class names, so this cannot clash with a user generated file name. I changed the conditional above to:

// need to precisely match the CompilerError message, otherwise a file name containing
// "CompilerError" may trigger an UnexpectedException (see bug 2872797)
if (message != null && message.startsWith("Compile exception: sun.tools.java.CompilerError"))
throw new UnexpectedException(message);

Two additional lessons to be learned from this are that we need to run our tests with both Java 5 and Java 6, and that we need to eat our dog food even more: We should compile DrJava inside DrJava.

Fixed as of revision 5105. I have made a new jar available: drjava-weekly-20091006-r5105.jar.

Share
Posted in Code Pranger, DrJava | Leave a comment

Print This Post Print This Post