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.
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | 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.