A while ago, I introduced what was supposed to be a “Smart Run” feature, a simpler way of running Java programs, ACM Java Task Force programs, and applets. It sort of worked, but it has been difficult to get it right: The code is concatenated in an ugly manner and interpreted, but since it’s still Java with its type system, I sort of get all the problems but none of the benefits of interpretation.
Now Paul Ezust, Professor of Mathematics and Computer Science at Suffolk University, has notified me of another problem: If the class a user is attempting to run is not marked public
then an exception is thrown.
With “Smart Run”, the code to start the program is a lot more complicated. DrJava needs to determine whether a program is an applet, and if it isn’t, it needs to check if it is an ACM Java Task Force program. In the last two situations, the main
method is then invoked using reflection (come to think of it, I could probably do this without reflection, too… I should try that).
If the class isn’t public, then Method.invoke
will throw an IllegalAccessException
unless I call setAccessible(true)
first. Now I do that, and I also handle the other exceptions better.
Here’s the concatenated command. Unwieldy, hm? I wish Java had multiline quotes.
String command =
"'{' boolean isProgram = false; boolean isApplet = false; Class c = {0}.class;\n" +
// cannot use Class.forName, doesn't work in Interactions Pane (see bug #1080869)
"while(c != null) '{'\n" +
" if (\"acm.program.Program\".equals(c.getName())) '{' isProgram = true; break; '}'\n" +
" c = c.getSuperclass();\n" +
"'}'\n" +
"if (!isProgram) '{'\n" +
" try '{'\n" +
// if this doesn't throw, {0} is a subclass of Applet
" {0}.class.asSubclass(java.applet.Applet.class);\n" +
" isApplet = true;\n" +
" '}' catch(ClassCastException cce) '{' '}'\n" +
"'}'\n" +
"if (isApplet) '{'\n" +
" edu.rice.cs.plt.swing.SwingUtil.showApplet(java.applet.Applet.class.cast(new {0}({1})), 400, 300);\n" +
"'}'\n" +
"else '{'" +
" java.lang.reflect.Method m = null;\n" +
" try '{'\n" +
" m = {0}.class.getMethod(\"main\", java.lang.String[].class);\n" +
" if (!m.getReturnType().equals(void.class)) throw new java.lang.NoSuchMethodException();\n" +
" '}'\n" +
" catch (java.lang.NoSuchMethodException e) '{'\n" +
" throw new java.lang.NoSuchMethodError(\"main\");\n" +
" '}'\n" +
" String[] args = new String[]'{'{1}'}';\n" +
" if (isProgram) '{'\n" +
" String[] newArgs = new String[args.length+1];\n" +
" newArgs[0] = \"code={0}\";\n" +
" System.arraycopy(args, 0, newArgs, 1, args.length);\n" +
" args = newArgs;\n" +
" '}'\n" +
" try '{'" +
" m.setAccessible(true);\n" +
" m.invoke(null, new Object[] '{' args '}');\n" +
" '}' catch(SecurityException se) '{'\n" +
" System.err.println(\"Error: Please turn off 'Smart Run' or use 'java' command instead of 'run'.\");\n" +
" '}' catch(IllegalAccessException iae) '{'\n" +
" System.err.println(\"Error: Please turn off 'Smart Run' or use 'java' command instead of 'run'.\");\n" +
" '}' catch(java.lang.reflect.InvocationTargetException ite) '{'\n" +
" if (ite.getCause()!=null) throw ite.getCause(); else\n" +
" System.err.println(\"Error: Please turn off 'Smart Run' or use 'java' command instead of 'run'.\");\n" +
"'}' '}' '}'";
I should try to rewrite this as Java code that doesn’t get interpreted in our Interactions Pane, but that actually gets statically compiled.
The bugfix is currently only available in our (more or less) DrJava weekly jar.