Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

2380
LINES

< > BotCompany Repo | #1001148 // x27.java (JavaX)

JavaX source code [tags: use-pretranspiled] - run with: x30.jar

Libraryless. Click here for Pure Java version (5151L/38K/105K).

1  
!747
2  
!awt {
3  
!actionListener {
4  
5  
/**
6  
 JavaX runner version 27
7  
8  
 Changes to v26:
9  
 -implementing new virtualization architecture
10  
 -javaCompilerOutput
11  
 -!post translators
12  
13  
 */
14  
15  
m { p { throw new RuntimeException("placebo"); } }
16  
17  
class x27 implements Runnable {
18  
  static final String version = "JavaX 27";
19  
20  
  // If programs run longer than this, they might have their class files
21  
  // deleted.
22  
  static int tempFileRetentionTime = 24; // hours
23  
  
24  
  static boolean verbose = false, translate = false, list = false, virtualizeTranslators = true;
25  
  static String translateTo = null;
26  
  static boolean preferCached = false, noID = false, noPrefetch = false, noAWT = false;
27  
  static boolean safeOnly = false, safeTranslate = false, javacOnly = false, logOn = true;
28  
  static boolean runMainInProcess = true, consoleOn = true, hasHelloMessage = false;
29  
  static List<String[]> mainTranslators = new ArrayList<String[]>();
30  
  private static Map<Long, String> memSnippetCache = new HashMap<Long, String>();
31  
  private static int processesStarted, compilations;
32  
33  
  // snippet ID -> md5
34  
  private static HashMap<Long, String> prefetched = new HashMap<Long, String>();
35  
  private static File virtCache;
36  
37  
  // doesn't work yet
38  
  private static Map<String, Class<?>> programCache = new HashMap<String, Class<?>>();
39  
  static boolean cacheTranslators = false;
40  
41  
  // this should work (caches transpiled translators)
42  
  private static HashMap<Long, Object[]> translationCache = new HashMap<Long, Object[]>();
43  
  static boolean cacheTranspiledTranslators = true;
44  
45  
  // which snippets are available pre-transpiled server-side?
46  
  private static Set<Long> hasTranspiledSet = new HashSet<Long>();
47  
  static boolean useServerTranspiled = true;
48  
49  
  static Object androidContext;
50  
  static boolean android = isAndroid();
51  
  
52  
  // We stick to 1.7 for now to support android.
53  
  // Scripts like #1001155 might change to 1.6
54  
  static S javaTarget = System.getProperty("java.version").startsWith("1.6.") ? "1.6" : "1.7";
55  
56  
  // Translators currently being translated (to detect recursions)
57  
  private static Set<Long> translating = new HashSet<Long>();
58  
59  
  static String lastOutput;
60  
  static String[] fullArgs;
61  
  private static Console console;
62  
  
63  
  static String javaCompilerOutput;
64  
65  
  public static void main(String[] args) {
66  
    try {
67  
      goMain(args);
68  
    } catch (Throwable e) {
69  
      e.printStackTrace();
70  
    }
71  
  }
72  
  
73  
  static void goMain(String[] args) throws Exception {
74  
    if (args.length != 0 && args[0].equals("-v")) verbose = true;
75  
    redirectSystemOutAndErr();
76  
77  
    for (S arg : args)
78  
      if (arg.equals("-noawt"))
79  
        noAWT = true;
80  
        
81  
    if (consoleOn && console == null && !noAWT)
82  
      tryToOpenConsole(args);
83  
    
84  
    String autoReport = loadTextFile(new File(userHome(), ".javax/auto-report-to-chat").getPath(), "").trim();
85  
    //print("autoReport=" + autoReport);
86  
    if (!isChatServer(args) && autoReport.equals("1"))
87  
      autoReportToChat();
88  
89  
    if (!hasHelloMessage) {
90  
      hasHelloMessage = true;
91  
      //installHelloMessage(args.length == 0 ? "JavaX Start-Up VM" : "JavaX VM (" + smartJoin(args) + ")");
92  
      makeVMAndroid();
93  
    }
94  
    
95  
    File ioBaseDir = new File("."), inputDir = null, outputDir = null;
96  
    String src = null;
97  
    List<String> programArgs = new ArrayList<String>();
98  
    fullArgs = args;
99  
100  
    for (int i = 0; i < args.length; i++) {
101  
      String arg = args[i];
102  
103  
      if (arg.equals("-version")) {
104  
        showVersion();
105  
        System.exit(0);
106  
      }
107  
108  
      if (arg.equals("-sysprop")) {
109  
        showSystemProperties();
110  
        return;
111  
      }
112  
113  
      if (arg.equals("-v") || arg.equals("-verbose"))
114  
        verbose = true;
115  
      else if (arg.equals("-finderror"))
116  
        verbose = true;
117  
      else if (arg.equals("-offline") || arg.equalsIgnoreCase("-prefercached"))
118  
        preferCached = true;
119  
      else if (arg.equals("-novirt"))
120  
        virtualizeTranslators = false;
121  
      else if (arg.equals("-safeonly"))
122  
        safeOnly = true;
123  
      else if (arg.equals("-safetranslate"))
124  
        safeTranslate = true;
125  
      else if (arg.equals("-noawt"))
126  
        noAWT = true;
127  
      else if (arg.equals("-noid"))
128  
        noID = true;
129  
      else if (arg.equals("-nocachetranspiled"))
130  
        cacheTranspiledTranslators = false;
131  
      else if (arg.equals("-javac"))
132  
        javacOnly = true;
133  
      else if (arg.equals("-localtranspile"))
134  
        useServerTranspiled = false;
135  
      else if (arg.equals("translate") && src == null)
136  
        translate = true;
137  
      else if (arg.equals("list") && src == null) {
138  
        list = true;
139  
        virtualizeTranslators = false; // so they are silenced
140  
      } else if (arg.equals("run") && src == null) {
141  
        // it's the default command anyway
142  
      } else if (arg.startsWith("input="))
143  
        inputDir = new File(arg.substring(6));
144  
      else if (arg.startsWith("output="))
145  
        outputDir = new File(arg.substring(7));
146  
      else if (arg.equals("with"))
147  
        mainTranslators.add(new String[] {args[++i], null});
148  
      else if (translate && arg.equals("to"))
149  
        translateTo = args[++i];
150  
      else if (src == null) {
151  
        //System.out.println("src=" + arg);
152  
        src = arg;
153  
      } else
154  
        programArgs.add(arg);
155  
    }
156  
157  
    cleanCache();
158  
159  
    if (useServerTranspiled)
160  
      noPrefetch = true;
161  
162  
    if (src == null) src = ".";
163  
164  
    // Might actually want to write to 2 disk caches (global/per program).
165  
    if (virtualizeTranslators && !preferCached)
166  
      virtCache = TempDirMaker_make();
167  
168  
    if (inputDir != null) {
169  
      ioBaseDir = TempDirMaker_make();
170  
      System.out.println("Taking input from: " + inputDir.getAbsolutePath());
171  
      System.out.println("Output is in: " + new File(ioBaseDir, "output").getAbsolutePath());
172  
      copyInput(inputDir, new File(ioBaseDir, "input"));
173  
    }
174  
    
175  
    if (logOn)
176  
      logStart(args);
177  
178  
    javaxmain(src, ioBaseDir, translate, list, programArgs.toArray(new String[programArgs.size()]));
179  
180  
    if (outputDir != null) {
181  
      copyInput(new File(ioBaseDir, "output"), outputDir);
182  
      System.out.println("Output copied to: " + outputDir.getAbsolutePath());
183  
    }
184  
185  
    if (verbose) {
186  
      // print stats
187  
      System.out.println("Processes started: " + processesStarted + ", compilations: " + compilations);
188  
    }
189  
  }
190  
191  
  public static void javaxmain(String src, File ioDir, boolean translate, boolean list,
192  
                               String[] args) throws Exception {
193  
    String programID = isSnippetID(src) ? "" + parseSnippetID(src) : null;
194  
    
195  
    if (programID != null)
196  
      System.err.println("JavaX TRANSLATE " + programID + " " + smartJoin(args));
197  
    
198  
    List<File> libraries = new ArrayList<File>();
199  
    File X = transpileMain(src, libraries);
200  
    if (verbose)
201  
      print("After transpileMain: " + X);
202  
      
203  
    if (X == null) {
204  
      showVersion();
205  
      
206  
      if (fullArgs != null) {
207  
        String[] nargs;
208  
        if (fullArgs.length == 0)
209  
          nargs = new String[] {"1000825"}; // swing-start
210  
        else {
211  
          // forward to search
212  
          nargs = new String[fullArgs.length+1];
213  
          nargs[0] = "636";
214  
          // nargs[1] = "search-runnables";
215  
          System.arraycopy(fullArgs, 0, nargs, 1, fullArgs.length);
216  
        }
217  
        main(nargs); // Hopefully we get no infinite recursion :)
218  
        return;
219  
      }
220  
      
221  
      System.out.println("No main.java found, exiting");
222  
      return;
223  
    }
224  
    
225  
    info.transpiledSrc = X;
226  
227  
    // list or run
228  
229  
    if (translate) {
230  
      File to = X;
231  
      if (translateTo != null) {
232  
        StringBuilder buf = new StringBuilder();
233  
        for (File f : libraries) buf.append(f.getName()+"\n");
234  
        if (new File(translateTo).isDirectory()) {
235  
          to = new File(translateTo, "main.java");
236  
          saveTextFile(new File(translateTo, "libraries.txt").getPath(), buf.toString());
237  
        } else {
238  
          to = new File(translateTo);
239  
          saveTextFile(new File(translateTo + "_libraries").getPath(), buf.toString());
240  
        }
241  
      }
242  
      if (to != X)
243  
        copy(new File(X, "main.java"), to);
244  
      System.out.println("Program translated to: " + to.getAbsolutePath());
245  
    } else if (list)
246  
      System.out.println(loadTextFile(new File(X, "main.java").getPath(), null));
247  
    else {
248  
      if (programID != null)
249  
        System.err.println("JavaX RUN " + programID + " " + smartJoin(args));
250  
      System.err.println(); // Make empty line before actual program starts
251  
      
252  
      javax2(X, ioDir, false, runMainInProcess, libraries, args, null, programID, info);
253  
      
254  
      System.out.println("[main done]");
255  
      
256  
      // cleanup reportToChat thread
257  
      if (reportToChat_q != null) {
258  
        if (customSystemOut != null)
259  
          Thread.sleep(1000); // delay to finish autoReportToChat. Yes it's hacky.
260  
        if (verbose) System.out.println("Closing reportToChat queue");
261  
        reportToChat_q.done();
262  
      }
263  
    }
264  
  }
265  
266  
  static File transpileMain(String src, List<File> libraries) throws Exception {
267  
    File srcDir;
268  
    boolean isTranspiled = false;
269  
    if (isSnippetID(src)) {
270  
      prefetch(src);
271  
      long id = parseSnippetID(src);
272  
      prefetched.remove(id); // hackfix to ensure transpiled main program is found.
273  
      srcDir = loadSnippetAsMainJava(src);
274  
      if (verbose)
275  
        System.err.println("hasTranspiledSet: " + hasTranspiledSet);
276  
      if (hasTranspiledSet.contains(id) && useServerTranspiled) {
277  
        //System.err.println("Trying pretranspiled main program: #" + id);
278  
        String transpiledSrc = getServerTranspiled("#" + id);
279  
        int i = transpiledSrc.indexOf('\n');
280  
        String libs = transpiledSrc.substring(0, Math.max(0, i));
281  
        transpiledSrc = transpiledSrc.substring(i+1);
282  
        if (!transpiledSrc.isEmpty()) {
283  
          srcDir = TempDirMaker_make();
284  
          saveTextFile(new File(srcDir, "main.java").getPath(), transpiledSrc);
285  
          isTranspiled = true;
286  
          //translationCache.put(id, new Object[] {srcDir, libraries});
287  
288  
          Matcher m = Pattern.compile("\\d+").matcher(libs);
289  
          while (m.find()) {
290  
            String libid = m.group();
291  
            File libraryFile = DiskSnippetCache_getLibrary(parseSnippetID(libid));
292  
            loadLibrary(libid, libraries, libraryFile);
293  
          }
294  
        }
295  
      }
296  
    } else {
297  
      srcDir = new File(src);
298  
299  
      // if the argument is a file, it is assumed to be main.java
300  
      if (srcDir.isFile()) {
301  
        srcDir = TempDirMaker_make();
302  
        copy(new File(src), new File(srcDir, "main.java"));
303  
      }
304  
305  
      if (!new File(srcDir, "main.java").exists())
306  
        return null;
307  
    }
308  
309  
    // translate
310  
311  
    File X = srcDir;
312  
313  
    if (!isTranspiled) {
314  
      X = topLevelTranslate(X, libraries);
315  
      System.err.println("Translated " + src);
316  
317  
      // save prefetch data
318  
      if (isSnippetID(src))
319  
        savePrefetchData(src);
320  
    }
321  
    return X;
322  
  }
323  
324  
  private static void prefetch(String mainSnippetID) throws IOException {
325  
    if (noPrefetch) return;
326  
327  
    long mainID = parseSnippetID(mainSnippetID);
328  
    String s = mainID + " " + loadTextFile(new File(userHome(), ".tinybrain/prefetch/" + mainID + ".txt").getPath(), "");
329  
    String[] ids = s.trim().split(" ");
330  
    if (ids.length > 1) {
331  
      String url = "http://tinybrain.de:8080/tb-int/prefetch.php?ids=" + URLEncoder.encode(s, "UTF-8");
332  
      String data = loadPage(new URL(url));
333  
      String[] split = data.split(" ");
334  
      if (split.length == ids.length)
335  
        for (int i = 0; i < ids.length; i++)
336  
          prefetched.put(parseSnippetID(ids[i]), split[i]);
337  
    }
338  
  }
339  
340  
  static String userHome() {
341  
    if (android)
342  
      return ((File) call(androidContext, "getFilesDir")).getAbsolutePath();
343  
    else
344  
      return System.getProperty("user.home");
345  
  }
346  
347  
  private static void savePrefetchData(String mainSnippetID) throws IOException {
348  
    List<String> ids = new ArrayList<String>();
349  
    long mainID = parseSnippetID(mainSnippetID);
350  
351  
    for (long id : memSnippetCache.keySet())
352  
      if (id != mainID)
353  
        ids.add(String.valueOf(id));
354  
355  
    saveTextFile(new File(userHome(),".tinybrain/prefetch/" + mainID + ".txt").getPath(), join(" ", ids));
356  
  }
357  
358  
  static File topLevelTranslate(File srcDir, List<File> libraries_out) throws Exception {
359  
    File X = srcDir;
360  
    X = applyTranslators(X, mainTranslators, libraries_out); // translators supplied on command line (unusual)
361  
362  
    // actual inner translation of the JavaX source
363  
    X = defaultTranslate(X, libraries_out);
364  
    return X;
365  
  }
366  
367  
  private static File defaultTranslate(File x, List<File> libraries_out) throws Exception {
368  
    x = luaPrintToJavaPrint(x);
369  
    x = repeatAutoTranslate(x, libraries_out);
370  
    return x;
371  
  }
372  
373  
  private static File repeatAutoTranslate(File x, List<File> libraries_out) throws Exception {
374  
    new List<String[]> postTranslators;
375  
    
376  
    while (true) {
377  
      String main = loadTextFile(new File(x, "main.java").getPath(), null);
378  
      List<String> lines = toLines(main);
379  
      List<String[]> t = findPostTranslators(lines);
380  
      postTranslators.addAll(t);
381  
      
382  
      if (!t.isEmpty()) {
383  
        main = fromLines(lines);
384  
        x = TempDirMaker_make();
385  
        saveTextFile(new File(x, "main.java").getPath(), main);
386  
      }
387  
388  
      File y = autoTranslate(x, libraries_out);
389  
      if (y == x)
390  
        break;
391  
      x = y;
392  
    }
393  
    
394  
    x = applyTranslators(x, postTranslators, libraries_out);
395  
    
396  
    return x;
397  
  }
398  
399  
  private static File autoTranslate(File x, List<File> libraries_out) throws Exception {
400  
    String main = loadTextFile(new File(x, "main.java").getPath(), null);
401  
    List<String> lines = toLines(main);
402  
    List<String[]> translators = findTranslators(lines);
403  
    if (translators.isEmpty())
404  
      return x;
405  
406  
    main = fromLines(lines);
407  
    File newDir = TempDirMaker_make();
408  
    saveTextFile(new File(newDir, "main.java").getPath(), main);
409  
    return applyTranslators(newDir, translators, libraries_out);
410  
  }
411  
412  
  static List<String[]> findTranslators(List<String> lines) {
413  
    List<String[]> translators = new ArrayList<String[]>();
414  
    Pattern pattern = Pattern.compile("^!([0-9# \t]+)");
415  
    Pattern pArgs = Pattern.compile("^\\s*\\((.*)\\)");
416  
    for (ListIterator<String> iterator = lines.listIterator(); iterator.hasNext(); ) {
417  
      String line = iterator.next();
418  
      line = line.trim();
419  
      Matcher matcher = pattern.matcher(line);
420  
      if (matcher.find()) {
421  
        String[] t = matcher.group(1).split("[ \t]+");
422  
        String rest = line.substring(matcher.end());
423  
        String arg = null;
424  
        if (t.length == 1) {
425  
          Matcher mArgs = pArgs.matcher(rest);
426  
          if (mArgs.find())
427  
            arg = mArgs.group(1);
428  
        }
429  
        for (String transi : t)
430  
          translators.add(new String[]{transi, arg});
431  
        iterator.remove();
432  
      }
433  
    }
434  
    return translators;
435  
  }
436  
437  
  static List<String[]> findPostTranslators(List<String> lines) {
438  
    List<String[]> translators = new ArrayList<String[]>();
439  
    Pattern pattern = Pattern.compile("^!post\\s*([0-9# \t]+)");
440  
    Pattern pArgs = Pattern.compile("^\\s*\\((.*)\\)");
441  
    for (ListIterator<String> iterator = lines.listIterator(); iterator.hasNext(); ) {
442  
      String line = iterator.next();
443  
      line = line.trim();
444  
      Matcher matcher = pattern.matcher(line);
445  
      if (matcher.find()) {
446  
        String[] t = matcher.group(1).split("[ \t]+");
447  
        String rest = line.substring(matcher.end());
448  
        String arg = null;
449  
        if (t.length == 1) {
450  
          Matcher mArgs = pArgs.matcher(rest);
451  
          if (mArgs.find())
452  
            arg = mArgs.group(1);
453  
        }
454  
        for (String transi : t)
455  
          translators.add(new String[]{transi, arg});
456  
        iterator.remove();
457  
      }
458  
    }
459  
    return translators;
460  
  }
461  
462  
  public static List<String> toLines(String s) {
463  
    List<String> lines = new ArrayList<String>();
464  
    int start = 0;
465  
    while (true) {
466  
      int i = toLines_nextLineBreak(s, start);
467  
      if (i < 0) {
468  
        if (s.length() > start) lines.add(s.substring(start));
469  
        break;
470  
      }
471  
472  
      lines.add(s.substring(start, i));
473  
      if (s.charAt(i) == '\r' && i+1 < s.length() && s.charAt(i+1) == '\n')
474  
        i += 2;
475  
      else
476  
        ++i;
477  
478  
      start = i;
479  
    }
480  
    return lines;
481  
  }
482  
483  
  private static int toLines_nextLineBreak(String s, int start) {
484  
    for (int i = start; i < s.length(); i++) {
485  
      char c = s.charAt(i);
486  
      if (c == '\r' || c == '\n')
487  
        return i;
488  
    }
489  
    return -1;
490  
  }
491  
492  
  public static String fromLines(List<String> lines) {
493  
    StringBuilder buf = new StringBuilder();
494  
    for (String line : lines) {
495  
      buf.append(line).append('\n');
496  
    }
497  
    return buf.toString();
498  
  }
499  
500  
  private static File applyTranslators(File x, List<String[]> translators, List<File> libraries_out) throws Exception {
501  
    for (String[] translator : translators)
502  
      x = applyTranslator(x, translator[0], translator[1], libraries_out);
503  
    return x;
504  
  }
505  
506  
  // also takes a library
507  
  private static File applyTranslator(File x, String translator, String arg, List<File> libraries_out) throws Exception {
508  
    if (verbose)
509  
      System.out.println("Using translator " + translator + " on sources in " + x.getPath());
510  
511  
    File newDir = runTranslatorOnInput(translator, null, arg, x, !verbose, libraries_out);
512  
513  
    if (!new File(newDir, "main.java").exists()) {
514  
      throw new Exception("Translator " + translator + " did not generate main.java");
515  
      // TODO: show translator output
516  
    }
517  
    if (verbose)
518  
      System.out.println("Translated with " + translator + " from " + x.getPath() + " to " + newDir.getPath());
519  
    x = newDir;
520  
    return x;
521  
  }
522  
523  
  private static File luaPrintToJavaPrint(File x) throws IOException {
524  
    File newDir = TempDirMaker_make();
525  
    String code = loadTextFile(new File(x, "main.java").getPath(), null);
526  
    code = luaPrintToJavaPrint(code);
527  
    saveTextFile(new File(newDir, "main.java").getPath(), code);
528  
    return newDir;
529  
  }
530  
531  
  public static String luaPrintToJavaPrint(String code) {
532  
    return ("\n" + code).replaceAll(
533  
      "(\n\\s*)print (\".*\")",
534  
      "$1System.out.println($2);").substring(1);
535  
  }
536  
537  
  public static File loadSnippetAsMainJava(String snippetID) throws IOException {
538  
    checkProgramSafety(snippetID);
539  
    File srcDir = TempDirMaker_make();
540  
    saveTextFile(new File(srcDir, "main.java").getPath(), loadSnippet(snippetID));
541  
    return srcDir;
542  
  }
543  
544  
  public static File loadSnippetAsMainJavaVerified(String snippetID, String hash) throws IOException {
545  
    checkProgramSafety(snippetID);
546  
    File srcDir = TempDirMaker_make();
547  
    saveTextFile(new File(srcDir, "main.java").getPath(), loadSnippetVerified(snippetID, hash));
548  
    return srcDir;
549  
  }
550  
551  
  @SuppressWarnings( "unchecked" )
552  
  /** returns output dir */
553  
  private static File runTranslatorOnInput(String snippetID, String hash, String arg, File input,
554  
                                           boolean silent,
555  
                                           List<File> libraries_out) throws Exception {
556  
    if (safeTranslate)
557  
      checkProgramSafetyImpl(snippetID);
558  
    long id = parseSnippetID(snippetID);
559  
560  
    // It's a library, not a translator.
561  
    File libraryFile = DiskSnippetCache_getLibrary(id);
562  
    if (verbose)
563  
      System.out.println("Library file for " + id + ": " + libraryFile);
564  
    if (libraryFile != null) {
565  
      loadLibrary(snippetID, libraries_out, libraryFile);
566  
      return input;
567  
    }
568  
569  
    String[] args = arg != null ? new String[]{arg} : new String[0];
570  
571  
    File srcDir = hash == null ? loadSnippetAsMainJava(snippetID)
572  
      : loadSnippetAsMainJavaVerified(snippetID, hash);
573  
    long mainJavaSize = new File(srcDir, "main.java").length();
574  
575  
    if (verbose)
576  
      System.out.println(snippetID + ": length = " + mainJavaSize);
577  
    if (mainJavaSize == 0) { // no text in snippet? assume it's a library
578  
      loadLibrary(snippetID, libraries_out, libraryFile);
579  
      return input;
580  
    }
581  
582  
    List<File> libraries = new ArrayList<File>();
583  
    Object[] cached = translationCache.get(id);
584  
    if (cached != null) {
585  
      //System.err.println("Taking translator " + snippetID + " from cache!");
586  
      srcDir = (File) cached[0];
587  
      libraries = (List<File>) cached[1];
588  
    } else if (hasTranspiledSet.contains(id) && useServerTranspiled) {
589  
      System.err.println("Trying pretranspiled translator: #" + snippetID);
590  
      String transpiledSrc = getServerTranspiled(snippetID);
591  
      transpiledSrc = transpiledSrc.substring(transpiledSrc.indexOf('\n')+1);
592  
      // TODO: check for libraries
593  
      if (!transpiledSrc.isEmpty()) {
594  
        srcDir = TempDirMaker_make();
595  
        saveTextFile(new File(srcDir, "main.java").getPath(), transpiledSrc);
596  
        translationCache.put(id, cached = new Object[] {srcDir, libraries});
597  
      }
598  
    }
599  
600  
    File ioBaseDir = TempDirMaker_make();
601  
602  
    /*Class<?> mainClass = programCache.get("" + parseSnippetID(snippetID));
603  
    if (mainClass != null)
604  
      return runCached(ioBaseDir, input, args);*/
605  
    // Doesn't work yet because virtualized directories are hardcoded in translator...
606  
607  
    if (cached == null) {
608  
      System.err.println("Translating translator #" + id);
609  
      if (translating.contains(id))
610  
        throw new RuntimeException("Recursive translator reference chain including #" + id);
611  
      translating.add(id);
612  
      try {
613  
        srcDir = defaultTranslate(srcDir, libraries);
614  
      } finally {
615  
        translating.remove(id);
616  
      }
617  
      System.err.println("Translated translator #" + id);
618  
      translationCache.put(id, new Object[]{srcDir, libraries});
619  
    }
620  
621  
    boolean runInProcess = false;
622  
623  
    if (virtualizeTranslators) {
624  
      if (verbose) System.out.println("Virtualizing translator");
625  
626  
      // TODO: don't virtualize class _javax (as included in, say, #636)
627  
628  
      //srcDir = applyTranslator(srcDir, "#2000351"); // I/O-virtualize the translator
629  
      // that doesn't work because it recurses infinitely...
630  
631  
      // So we do it right here:
632  
      String s = loadTextFile(new File(srcDir, "main.java").getPath(), null);
633  
      s = s.replaceAll("new\\s+File\\(", "virtual.newFile(");
634  
      s = s.replaceAll("new\\s+FileInputStream\\(", "virtual.newFileInputStream(");
635  
      s = s.replaceAll("new\\s+FileOutputStream\\(", "virtual.newFileOutputStream(");
636  
      s += "\n\n" + loadSnippet("#2000355"); // load class virtual
637  
638  
      // forward snippet cache (virtualized one)
639  
      File dir = virtCache != null ? virtCache : DiskSnippetCache_dir;
640  
      s = s.replace("static File DiskSnippetCache_dir" + ";",
641  
        "static File DiskSnippetCache_dir " + "= new File(" + javaQuote(dir.getAbsolutePath()) + ");"); // extra + is necessary for Dumb TinyBrain :)
642  
      s = s.replace("static boolean preferCached = false;", "static boolean preferCached = true;");
643  
644  
      if (verbose) {
645  
        System.out.println("==BEGIN VIRTUALIZED TRANSLATOR==");
646  
        System.out.println(s);
647  
        System.out.println("==END VIRTUALIZED TRANSLATOR==");
648  
      }
649  
      srcDir = TempDirMaker_make();
650  
      saveTextFile(new File(srcDir, "main.java").getPath(), s);
651  
652  
      // TODO: silence translator also
653  
      runInProcess = true;
654  
    }
655  
656  
    return runJavaX(ioBaseDir, srcDir, input, silent, runInProcess, libraries,
657  
      args, cacheTranslators ? "" + id : null, "" + id);
658  
  }
659  
660  
  private static String getServerTranspiled(String snippetID) throws IOException {
661  
    long id = parseSnippetID(snippetID);
662  
    URL url = new URL("http://tinybrain.de:8080/tb-int/get-transpiled.php?raw=1&withlibs=1&id=" + id);
663  
    return loadPage(url);
664  
  }
665  
666  
  static void checkProgramSafety(String snippetID) throws IOException {
667  
    if (!safeOnly) return;
668  
    checkProgramSafetyImpl(snippetID);
669  
  }
670  
671  
  static void checkProgramSafetyImpl(String snippetID) throws IOException {
672  
    URL url = new URL("http://tinybrain.de:8080/tb-int/is-javax-safe.php?id=" + parseSnippetID(snippetID));
673  
    String text = loadPage(url);
674  
    if (!text.startsWith("{\"safe\":\"1\"}"))
675  
      throw new RuntimeException("Program not safe: #" + parseSnippetID(snippetID));
676  
  }
677  
678  
  static void loadLibrary(String snippetID, List<File> libraries_out, File libraryFile) throws IOException {
679  
    if (verbose)
680  
      System.out.println("Assuming " + snippetID + " is a library.");
681  
682  
    if (libraryFile == null) {
683  
      byte[] data = loadDataSnippetImpl(snippetID);
684  
      DiskSnippetCache_putLibrary(parseSnippetID(snippetID), data);
685  
      libraryFile = DiskSnippetCache_getLibrary(parseSnippetID(snippetID));
686  
    }
687  
688  
    if (!libraries_out.contains(libraryFile))
689  
      libraries_out.add(libraryFile);
690  
  }
691  
692  
  private static byte[] loadDataSnippetImpl(String snippetID) throws IOException {
693  
    byte[] data;
694  
    try {
695  
      URL url = new URL("http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_"
696  
        + parseSnippetID(snippetID) + "&contentType=application/binary");
697  
      System.err.println("Loading library: " + url);
698  
      data = loadBinaryPage(url.openConnection());
699  
      if (verbose)
700  
        System.err.println("Bytes loaded: " + data.length);
701  
    } catch (FileNotFoundException e) {
702  
      throw new IOException("Binary snippet #" + snippetID + " not found or not public");
703  
    }
704  
    return data;
705  
  }
706  
707  
  /** returns output dir */
708  
  private static File runJavaX(File ioBaseDir, File originalSrcDir, File originalInput,
709  
                               boolean silent, boolean runInProcess,
710  
                               List<File> libraries, String[] args, String cacheAs,
711  
                               String programID) throws Exception {
712  
    File srcDir = new File(ioBaseDir, "src");
713  
    File inputDir = new File(ioBaseDir, "input");
714  
    File outputDir = new File(ioBaseDir, "output");
715  
    copyInput(originalSrcDir, srcDir);
716  
    copyInput(originalInput, inputDir);
717  
    javax2(srcDir, ioBaseDir, silent, runInProcess, libraries, args, cacheAs, programID, null);
718  
    return outputDir;
719  
  }
720  
721  
  private static void copyInput(File src, File dst) throws IOException {
722  
    copyDirectory(src, dst);
723  
  }
724  
725  
  public static boolean hasFile(File inputDir, String name) {
726  
    return new File(inputDir, name).exists();
727  
  }
728  
729  
  public static void copyDirectory(File src, File dst) throws IOException {
730  
    if (verbose) System.out.println("Copying " + src.getAbsolutePath() + " to " + dst.getAbsolutePath());
731  
    dst.mkdirs();
732  
    File[] files = src.listFiles();
733  
    if (files == null) return;
734  
    for (File file : files) {
735  
      File dst1 = new File(dst, file.getName());
736  
      if (file.isDirectory())
737  
        copyDirectory(file, dst1);
738  
      else {
739  
        if (verbose) System.out.println("Copying " + file.getAbsolutePath() + " to " + dst1.getAbsolutePath());
740  
        copy(file, dst1);
741  
      }
742  
    }
743  
  }
744  
745  
  /** Quickly copy a file without a progress bar or any other fancy GUI... :) */
746  
  public static void copy(File src, File dest) throws IOException {
747  
    FileInputStream inputStream = newFileInputStream(src);
748  
    FileOutputStream outputStream = newFileOutputStream(dest);
749  
    try {
750  
      copy(inputStream, outputStream);
751  
      inputStream.close();
752  
    } finally {
753  
      outputStream.close();
754  
    }
755  
  }
756  
757  
  static Object call(Object o, String method, Object... args) {
758  
    try {
759  
      Method m = call_findMethod(o, method, args, false);
760  
      m.setAccessible(true);
761  
      return m.invoke(o, args);
762  
    } catch (Exception e) {
763  
      throw new RuntimeException(e);
764  
    }
765  
  }
766  
767  
  static Object call(Class c, String method, Object... args) {
768  
    try {
769  
      Method m = call_findStaticMethod(c, method, args, false);
770  
      m.setAccessible(true);
771  
      return m.invoke(null, args);
772  
    } catch (Exception e) {
773  
      throw new RuntimeException(e);
774  
    }
775  
  }
776  
777  
  static Method call_findStaticMethod(Class c, String method, Object[] args, boolean debug) {
778  
    while (c != null) {
779  
      for (Method m : c.getDeclaredMethods()) {
780  
        if (debug)
781  
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
782  
        if (!m.getName().equals(method)) {
783  
          if (debug) System.out.println("Method name mismatch: " + method);
784  
          continue;
785  
        }
786  
787  
        if ((m.getModifiers() & Modifier.STATIC) == 0 || !call_checkArgs(m, args, debug))
788  
          continue;
789  
790  
        return m;
791  
      }
792  
      c = c.getSuperclass();
793  
    }
794  
    throw new RuntimeException("Method '" + method + "' (static) with " + args.length + " parameter(s) not found in " + c.getName());
795  
  }
796  
797  
  static Method call_findMethod(Object o, String method, Object[] args, boolean debug) {
798  
    Class c = o.getClass();
799  
    while (c != null) {
800  
      for (Method m : c.getDeclaredMethods()) {
801  
        if (debug)
802  
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
803  
        if (m.getName().equals(method) && call_checkArgs(m, args, debug))
804  
          return m;
805  
      }
806  
      c = c.getSuperclass();
807  
    }
808  
    throw new RuntimeException("Method '" + method + "' (non-static) with " + args.length + " parameter(s) not found in " + o.getClass().getName());
809  
  }
810  
811  
  private static boolean call_checkArgs(Method m, Object[] args, boolean debug) {
812  
    Class<?>[] types = m.getParameterTypes();
813  
    if (types.length != args.length) {
814  
      if (debug)
815  
        System.out.println("checkArgs: Bad parameter length: " + args.length + " vs " + types.length);
816  
      return false;
817  
    }
818  
    for (int i = 0; i < types.length; i++)
819  
      if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
820  
        if (debug)
821  
          System.out.println("checkArgs: Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
822  
        return false;
823  
      }
824  
    return true;
825  
  }
826  
827  
  private static FileInputStream newFileInputStream(File f) throws FileNotFoundException {
828  
    /*if (androidContext != null)
829  
      return (FileInputStream) call(androidContext,
830  
        "openFileInput", f.getPath());
831  
    else*/
832  
    return new // line break for Dumb TinyBrain :)
833  
    FileInputStream(f);
834  
  }
835  
836  
  private static FileOutputStream newFileOutputStream(File f) throws FileNotFoundException {
837  
    /*if (androidContext != null)
838  
      return (FileOutputStream) call(androidContext,
839  
        "openFileOutput", f.getPath(), 0);
840  
    else*/
841  
    return new // line break for Dumb TinyBrain :)
842  
    FileOutputStream(f);
843  
  }
844  
845  
  public static void copy(InputStream in, OutputStream out) throws IOException {
846  
    byte[] buf = new byte[65536];
847  
    while (true) {
848  
      int n = in.read(buf);
849  
      if (n <= 0) return;
850  
      out.write(buf, 0, n);
851  
    }
852  
  }
853  
854  
  /** writes safely (to temp file, then rename) */
855  
  public static void saveTextFile(String fileName, String contents) throws IOException {
856  
    File file = new File(fileName);
857  
    File parentFile = file.getParentFile();
858  
    if (parentFile != null)
859  
      parentFile.mkdirs();
860  
    String tempFileName = fileName + "_temp";
861  
    FileOutputStream fileOutputStream = newFileOutputStream(new File(tempFileName));
862  
    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, charsetForTextFiles);
863  
    PrintWriter printWriter = new PrintWriter(outputStreamWriter);
864  
    printWriter.print(contents);
865  
    printWriter.close();
866  
    if (file.exists() && !file.delete())
867  
      throw new IOException("Can't delete " + fileName);
868  
869  
    if (!new File(tempFileName).renameTo(file))
870  
      throw new IOException("Can't rename " + tempFileName + " to " + fileName);
871  
  }
872  
873  
  /** writes safely (to temp file, then rename) */
874  
  public static void saveBinaryFile(String fileName, byte[] contents) throws IOException {
875  
    File file = new File(fileName);
876  
    File parentFile = file.getParentFile();
877  
    if (parentFile != null)
878  
      parentFile.mkdirs();
879  
    String tempFileName = fileName + "_temp";
880  
    FileOutputStream fileOutputStream = newFileOutputStream(new File(tempFileName));
881  
    fileOutputStream.write(contents);
882  
    fileOutputStream.close();
883  
    if (file.exists() && !file.delete())
884  
      throw new IOException("Can't delete " + fileName);
885  
886  
    if (!new File(tempFileName).renameTo(file))
887  
      throw new IOException("Can't rename " + tempFileName + " to " + fileName);
888  
  }
889  
890  
  !include #1001049 // loadTextFile
891  
  
892  
  static File DiskSnippetCache_dir;
893  
894  
  public static void initDiskSnippetCache(File dir) {
895  
    DiskSnippetCache_dir = dir;
896  
    dir.mkdirs();
897  
  }
898  
899  
  // Data files are immutable, use centralized cache
900  
  public static synchronized File DiskSnippetCache_getLibrary(long snippetID) throws IOException {
901  
    File file = new File(getGlobalCache(), "data_" + snippetID + ".jar");
902  
    if (verbose)
903  
      System.out.println("Checking data cache: " + file.getPath());
904  
    return file.exists() ? file : null;
905  
  }
906  
907  
  public static synchronized String DiskSnippetCache_get(long snippetID) throws IOException {
908  
    return loadTextFile(DiskSnippetCache_getFile(snippetID).getPath(), null);
909  
  }
910  
911  
  private static File DiskSnippetCache_getFile(long snippetID) {
912  
    return new File(DiskSnippetCache_dir, "" + snippetID);
913  
  }
914  
915  
  public static synchronized void DiskSnippetCache_put(long snippetID, String snippet) throws IOException {
916  
    saveTextFile(DiskSnippetCache_getFile(snippetID).getPath(), snippet);
917  
  }
918  
919  
  public static synchronized void DiskSnippetCache_putLibrary(long snippetID, byte[] data) throws IOException {
920  
    saveBinaryFile(new File(getGlobalCache(), "data_" + snippetID).getPath() + ".jar", data);
921  
  }
922  
923  
  public static File DiskSnippetCache_getDir() {
924  
    return DiskSnippetCache_dir;
925  
  }
926  
927  
  public static void initSnippetCache() {
928  
    if (DiskSnippetCache_dir == null)
929  
      initDiskSnippetCache(getGlobalCache());
930  
  }
931  
932  
  private static File getGlobalCache() {
933  
    File file = new File(userHome(), ".tinybrain/snippet-cache");
934  
    file.mkdirs();
935  
    return file;
936  
  }
937  
938  
  public static String loadSnippetVerified(String snippetID, String hash) throws IOException {
939  
    String text = loadSnippet(snippetID);
940  
    String realHash = getHash(text.getBytes("UTF-8"));
941  
    if (!realHash.equals(hash)) {
942  
      String msg;
943  
      if (hash.isEmpty())
944  
        msg = "Here's your hash for " + snippetID + ", please put in your program: " + realHash;
945  
      else
946  
        msg = "Hash mismatch for " + snippetID + ": " + realHash + " (new) vs " + hash + " - has tinybrain.de been hacked??";
947  
      throw new RuntimeException(msg);
948  
    }
949  
    return text;
950  
  }
951  
952  
  public static String getHash(byte[] data) {
953  
    return bytesToHex(getFullFingerprint(data));
954  
  }
955  
956  
  public static byte[] getFullFingerprint(byte[] data) {
957  
    try {
958  
      return MessageDigest.getInstance("MD5").digest(data);
959  
    } catch (NoSuchAlgorithmException e) {
960  
      throw new RuntimeException(e);
961  
    }
962  
  }
963  
964  
  public static String bytesToHex(byte[] bytes) {
965  
    return bytesToHex(bytes, 0, bytes.length);
966  
  }
967  
968  
  public static String bytesToHex(byte[] bytes, int ofs, int len) {
969  
    StringBuilder stringBuilder = new StringBuilder(len*2);
970  
    for (int i = 0; i < len; i++) {
971  
      String s = "0" + Integer.toHexString(bytes[ofs+i]);
972  
      stringBuilder.append(s.substring(s.length()-2, s.length()));
973  
    }
974  
    return stringBuilder.toString();
975  
  }
976  
977  
  public static String loadSnippet(String snippetID) throws IOException {
978  
    return loadSnippet(parseSnippetID(snippetID));
979  
  }
980  
981  
  public static long parseSnippetID(String snippetID) {
982  
    return Long.parseLong(shortenSnippetID(snippetID));
983  
  }
984  
985  
  private static String shortenSnippetID(String snippetID) {
986  
    if (snippetID.startsWith("#"))
987  
      snippetID = snippetID.substring(1);
988  
    String httpBlaBla = "http://tinybrain.de/";
989  
    if (snippetID.startsWith(httpBlaBla))
990  
      snippetID = snippetID.substring(httpBlaBla.length());
991  
    return snippetID;
992  
  }
993  
994  
  public static boolean isSnippetID(String snippetID) {
995  
    snippetID = shortenSnippetID(snippetID);
996  
    return isInteger(snippetID) && Long.parseLong(snippetID) != 0;
997  
  }
998  
999  
  public static boolean isInteger(String s) {
1000  
    return Pattern.matches("\\-?\\d+", s);
1001  
  }
1002  
1003  
  public static String loadSnippet(long snippetID) throws IOException {
1004  
    String text = memSnippetCache.get(snippetID);
1005  
    if (text != null) {
1006  
      if (verbose)
1007  
        System.out.println("Getting " + snippetID + " from mem cache");
1008  
      return text;
1009  
    }
1010  
1011  
    initSnippetCache();
1012  
    text = DiskSnippetCache_get(snippetID);
1013  
    if (preferCached && text != null) {
1014  
      if (verbose)
1015  
        System.out.println("Getting " + snippetID + " from disk cache (preferCached)");
1016  
      return text;
1017  
    }
1018  
1019  
    String md5 = text != null ? md5(text) : "-";
1020  
    if (text != null) {
1021  
      String hash = prefetched.get(snippetID);
1022  
      if (hash != null) {
1023  
        if (md5.equals(hash)) {
1024  
          memSnippetCache.put(snippetID, text);
1025  
          if (verbose)
1026  
            System.out.println("Getting " + snippetID + " from prefetched");
1027  
          return text;
1028  
        } else
1029  
          prefetched.remove(snippetID); // (maybe this is not necessary)
1030  
      }
1031  
    }
1032  
1033  
    try {
1034  
      /*URL url = new URL("http://tinybrain.de:8080/getraw.php?id=" + snippetID);
1035  
      text = loadPage(url);*/
1036  
      String theURL = "http://tinybrain.de:8080/getraw.php?id=" + snippetID + "&getmd5=1&utf8=1&usetranspiled=1";
1037  
      if (text != null) {
1038  
        //System.err.println("MD5: " + md5);
1039  
        theURL += "&md5=" + md5;
1040  
      }
1041  
      URL url = new URL(theURL);
1042  
      String page = loadPage(url);
1043  
1044  
      // parse & drop transpilation flag available line
1045  
      int i = page.indexOf('\n');
1046  
      boolean hasTranspiled = page.substring(0, i).trim().equals("1");
1047  
      if (hasTranspiled)
1048  
        hasTranspiledSet.add(snippetID);
1049  
      else
1050  
        hasTranspiledSet.remove(snippetID);
1051  
      page = page.substring(i+1);
1052  
1053  
      if (page.startsWith("==*#*==")) {
1054  
        // same, keep text
1055  
        //System.err.println("Snippet unchanged, keeping.");
1056  
      } else {
1057  
        // drop md5 line
1058  
        i = page.indexOf('\n');
1059  
        String hash = page.substring(0, i).trim();
1060  
        text = page.substring(i+1);
1061  
1062  
        String myHash = md5(text);
1063  
        if (myHash.equals(hash)) {
1064  
          //System.err.println("Hash match: " + hash);
1065  
        } else
1066  
          System.err.println("Hash mismatch");
1067  
      }
1068  
    } catch (FileNotFoundException e) {
1069  
      e.printStackTrace();
1070  
      throw new IOException("Snippet #" + snippetID + " not found or not public");
1071  
    }
1072  
1073  
    memSnippetCache.put(snippetID, text);
1074  
1075  
    try {
1076  
      initSnippetCache();
1077  
      DiskSnippetCache_put(snippetID, text);
1078  
    } catch (IOException e) {
1079  
      System.err.println("Minor warning: Couldn't save snippet to cache ("  + DiskSnippetCache_getDir() + ")");
1080  
    }
1081  
1082  
    return text;
1083  
  }
1084  
1085  
  private static String md5(String text) {
1086  
    try {
1087  
      return bytesToHex(md5impl(text.getBytes("UTF-8"))); // maybe different than the way PHP does it...
1088  
    } catch (UnsupportedEncodingException e) {
1089  
      throw new RuntimeException(e);
1090  
    }
1091  
  }
1092  
1093  
  public static byte[] md5impl(byte[] data) {
1094  
    try {
1095  
      return MessageDigest.getInstance("MD5").digest(data);
1096  
    } catch (NoSuchAlgorithmException e) {
1097  
      throw new RuntimeException(e);
1098  
    }
1099  
  }
1100  
1101  
  private static String loadPage(URL url) throws IOException {
1102  
    System.err.println("Loading: " + url.toExternalForm());
1103  
    URLConnection con = url.openConnection();
1104  
    return loadPage(con, url);
1105  
  }
1106  
1107  
  public static String loadPage(URLConnection con, URL url) throws IOException {
1108  
    setHeaders(con);
1109  
    String contentType = con.getContentType();
1110  
    if (contentType == null)
1111  
      throw new IOException("Page could not be read: " + url);
1112  
    //Log.info("Content-Type: " + contentType);
1113  
    String charset = guessCharset(contentType);
1114  
    //System.err.println("Charset: " + charset);
1115  
    Reader r = new InputStreamReader(con.getInputStream(), charset);
1116  
    StringBuilder buf = new StringBuilder();
1117  
    while (true) {
1118  
      int ch = r.read();
1119  
      if (ch < 0)
1120  
        break;
1121  
      //Log.info("Chars read: " + buf.length());
1122  
      buf.append((char) ch);
1123  
    }
1124  
    return buf.toString();
1125  
  }
1126  
1127  
  public static byte[] loadBinaryPage(URLConnection con) throws IOException {
1128  
    setHeaders(con);
1129  
    return loadBinaryPage_noHeaders(con);
1130  
  }
1131  
1132  
  private static byte[] loadBinaryPage_noHeaders(URLConnection con) throws IOException {
1133  
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
1134  
    InputStream inputStream = con.getInputStream();
1135  
    while (true) {
1136  
      int ch = inputStream.read();
1137  
      if (ch < 0)
1138  
        break;
1139  
      buf.write(ch);
1140  
    }
1141  
    inputStream.close();
1142  
    return buf.toByteArray();
1143  
  }
1144  
1145  
  private static void setHeaders(URLConnection con) throws IOException {
1146  
    String computerID = getComputerID();
1147  
    if (computerID != null)
1148  
      con.setRequestProperty("X-ComputerID", computerID);
1149  
  }
1150  
1151  
  public static String guessCharset(String contentType) {
1152  
    Pattern p = Pattern.compile("text/html;\\s+charset=([^\\s]+)\\s*");
1153  
    Matcher m = p.matcher(contentType);
1154  
    /* If Content-Type doesn't match this pre-conception, choose default and hope for the best. */
1155  
    return m.matches() ? m.group(1) : "ISO-8859-1";
1156  
  }
1157  
1158  
  /** runs a transpiled set of sources */
1159  
  public static void javax2(File srcDir, File ioBaseDir, boolean silent, boolean runInProcess,
1160  
                            List<File> libraries, String[] args, String cacheAs,
1161  
                            String programID, Info info) throws Exception {
1162  
    if (android) {
1163  
      // TODO: no translator virtualization? huh?
1164  
      javax2android(srcDir, args, programID);
1165  
    } else {
1166  
      File classesDir = TempDirMaker_make();
1167  
      String javacOutput = compileJava(srcDir, libraries, classesDir);
1168  
1169  
      // run
1170  
1171  
      if (verbose) System.out.println("Running program (" + srcDir.getAbsolutePath()
1172  
        + ") on io dir " + ioBaseDir.getAbsolutePath() + (runInProcess ? "[in-process]" : "") + "\n");
1173  
      runProgram(javacOutput, classesDir, ioBaseDir, silent, runInProcess, libraries, args, cacheAs, programID, info);
1174  
    }
1175  
  }
1176  
1177  
  static Class<?> loadx2android(File srcDir, String programID) throws Exception {
1178  
    // TODO: optimize if it's a loaded snippet anyway
1179  
    URL url = new URL("http://tinybrain.de:8080/dexcompile.php");
1180  
    URLConnection conn = url.openConnection();
1181  
    String postData = "src=" + URLEncoder.encode(loadTextFile(new File(srcDir, "main.java").getPath(), null), "UTF-8");
1182  
    byte[] dexData = doPostBinary(postData, conn);
1183  
    if (!isDex(dexData))
1184  
      throw new RuntimeException("Dex generation error: " + dexData.length + " bytes - " + new String(dexData, "UTF-8"));
1185  
    System.out.println("Dex loaded: " + dexData.length + "b");
1186  
1187  
    File dexDir = TempDirMaker_make();
1188  
    File dexFile = new File(dexDir, System.currentTimeMillis() + ".dex");
1189  
    File dexOutputDir = TempDirMaker_make();
1190  
1191  
    System.out.println("Saving dex to: " + dexDir.getAbsolutePath());
1192  
    try {
1193  
      saveBinaryFile(dexFile.getPath(), dexData);
1194  
    } catch (Throwable e) {
1195  
      System.out.println("Whoa!");
1196  
      throw new RuntimeException(e);
1197  
    }
1198  
1199  
    System.out.println("Getting parent class loader.");
1200  
    ClassLoader parentClassLoader =
1201  
      //ClassLoader.getSystemClassLoader(); // does not find support jar
1202  
      //getClass().getClassLoader(); // Let's try this...
1203  
      x27.class.getClassLoader().getParent(); // XXX !
1204  
1205  
    //System.out.println("Making DexClassLoader.");
1206  
    //DexClassLoader classLoader = new DexClassLoader(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null,
1207  
    //  parentClassLoader);
1208  
    Class dcl = Class.forName("dalvik.system.DexClassLoader");
1209  
    Object classLoader = dcl.getConstructors()[0].newInstance(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null,
1210  
      parentClassLoader);
1211  
1212  
    //System.out.println("Loading main class.");
1213  
    //Class<?> theClass = classLoader.loadClass(mainClassName);
1214  
    Class<?> theClass = (Class<?>) call(classLoader, "loadClass", "main");
1215  
1216  
    //System.out.println("Main class loaded.");
1217  
    try {
1218  
      set(theClass, "androidContext", androidContext);
1219  
    } catch (Throwable e) {}
1220  
1221  
    setVars(theClass, programID);
1222  
1223  
    return theClass;
1224  
  }
1225  
1226  
  static void javax2android(File srcDir, String[] args, String programID) throws Exception {
1227  
    Class<?> theClass = loadx2android(srcDir, programID);
1228  
1229  
    Method main = null;
1230  
    try {
1231  
      main = call_findStaticMethod(theClass, "main", new Object[]{androidContext}, false);
1232  
    } catch (RuntimeException e) {
1233  
    }
1234  
1235  
    //System.out.println("main method for " + androidContext + " of " + theClass + ": " + main);
1236  
1237  
    if (main != null) {
1238  
      // old style main program that returns a View
1239  
      System.out.println("Calling main (old-style)");
1240  
      Object view = main.invoke(null, androidContext);
1241  
      System.out.println("Calling setContentView with " + view);
1242  
      call(Class.forName("main"), "setContentViewInUIThread", view);
1243  
      //call(androidContext, "setContentView", view);
1244  
      System.out.println("Done.");
1245  
    } else {
1246  
      System.out.println("New-style main method running.\n\n====\n");
1247  
      runMainMethod(args, theClass);
1248  
    }
1249  
  }
1250  
1251  
  static byte[] DEX_FILE_MAGIC = { 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 };
1252  
1253  
  static boolean isDex(byte[] dexData) {
1254  
    if (dexData.length < DEX_FILE_MAGIC.length) return false;
1255  
    for (int i = 0; i < DEX_FILE_MAGIC.length; i++)
1256  
      if (dexData[i] != DEX_FILE_MAGIC[i])
1257  
        return false;
1258  
    return true;
1259  
  }
1260  
1261  
  static byte[] doPostBinary(String urlParameters, URLConnection conn) throws IOException {
1262  
    // connect and do POST
1263  
    setHeaders(conn);
1264  
    conn.setDoOutput(true);
1265  
1266  
    OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
1267  
    writer.write(urlParameters);
1268  
    writer.flush();
1269  
1270  
    byte[] contents = loadBinaryPage_noHeaders(conn);
1271  
    writer.close();
1272  
    return contents;
1273  
  }
1274  
1275  
  static String compileJava(File srcDir, List<File> libraries, File classesDir) throws IOException {
1276  
    javaCompilerOutput = null;
1277  
    ++compilations;
1278  
1279  
    // collect sources
1280  
1281  
    List<File> sources = new ArrayList<File>();
1282  
    if (verbose) System.out.println("Scanning for sources in " + srcDir.getPath());
1283  
    scanForSources(srcDir, sources, true);
1284  
    if (sources.isEmpty())
1285  
      throw new IOException("No sources found");
1286  
1287  
    // compile
1288  
1289  
    File optionsFile = File.createTempFile("javax", "");
1290  
    if (verbose) System.out.println("Compiling " + sources.size() + " source(s) to " + classesDir.getPath());
1291  
    if (verbose) System.out.println("Libraries: " + libraries);
1292  
    String options = "-d " + bashQuote(classesDir.getPath());
1293  
    writeOptions(sources, libraries, optionsFile, options);
1294  
    classesDir.mkdirs();
1295  
    return javaCompilerOutput = invokeJavaCompiler(optionsFile);
1296  
  }
1297  
1298  
  private static void runProgram(String javacOutput, File classesDir, File ioBaseDir,
1299  
                                 boolean silent, boolean runInProcess,
1300  
                                 List<File> libraries, String[] args, String cacheAs,
1301  
                                 String programID, Info info) throws Exception {
1302  
    // print javac output if compile failed and it hasn't been printed yet
1303  
    if (info != null) {
1304  
      info.programID = programID;
1305  
      info.programArgs = args;
1306  
    }
1307  
    boolean didNotCompile = !didCompile(classesDir);
1308  
    if (verbose || didNotCompile)
1309  
      System.out.println(javacOutput);
1310  
    if (didNotCompile)
1311  
      return;
1312  
1313  
    if (runInProcess
1314  
      || (ioBaseDir.getAbsolutePath().equals(new File(".").getAbsolutePath()) && !silent)) {
1315  
      runProgramQuick(classesDir, libraries, args, cacheAs, programID, info, ioBaseDir);
1316  
      return;
1317  
    }
1318  
1319  
    boolean echoOK = false;
1320  
    // TODO: add libraries to class path
1321  
    String bashCmd = "(cd " + bashQuote(ioBaseDir.getAbsolutePath()) + " && (java -cp "
1322  
      + bashQuote(classesDir.getAbsolutePath()) + " main" + (echoOK ? "; echo ok" : "") + "))";
1323  
    if (verbose) System.out.println(bashCmd);
1324  
    String output = backtick(bashCmd);
1325  
    lastOutput = output;
1326  
    if (verbose || !silent)
1327  
      System.out.println(output);
1328  
  }
1329  
1330  
  static boolean didCompile(File classesDir) {
1331  
    return hasFile(classesDir, "main.class");
1332  
  }
1333  
1334  
  private static void runProgramQuick(File classesDir, List<File> libraries,
1335  
                                      String[] args, String cacheAs,
1336  
                                      String programID, Info info,
1337  
                                      File ioBaseDir) throws Exception {
1338  
    // collect urls
1339  
    URL[] urls = new URL[libraries.size()+1];
1340  
    urls[0] = classesDir.toURI().toURL();
1341  
    for (int i = 0; i < libraries.size(); i++)
1342  
      urls[i+1] = libraries.get(i).toURI().toURL();
1343  
1344  
    // make class loader
1345  
    URLClassLoader classLoader = new URLClassLoader(urls);
1346  
1347  
    // load JavaX main class
1348  
    Class<?> mainClass = classLoader.loadClass("main");
1349  
    
1350  
    if (info != null)
1351  
      info.mainClass = mainClass;
1352  
      
1353  
    if (cacheAs != null)
1354  
      programCache.put(cacheAs, mainClass);
1355  
      
1356  
    // change baseDir
1357  
    try {
1358  
      //print("Changing base dir to " + ioBaseDir.getAbsolutePath());
1359  
      Class virtual = mainClass.getClassLoader().loadClass("virtual");
1360  
      set(virtual, "virtual_baseDir", ioBaseDir.getAbsolutePath());
1361  
    } catch (Throwable e) { /* whatever */ }
1362  
1363  
    setVars(mainClass, programID);
1364  
    runMainMethod(args, mainClass);
1365  
  }
1366  
1367  
  static void setVars(Class<?> theClass, String programID) {
1368  
    try {
1369  
      set(theClass, "programID", programID);
1370  
    } catch (Throwable e) {}
1371  
1372  
    try {
1373  
      set(theClass, "__javax", x27.class);
1374  
    } catch (Throwable e) {}
1375  
  }
1376  
1377  
1378  
  static void runMainMethod(Object args, Class<?> mainClass) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
1379  
    Method main = mainClass.getMethod("main", String[].class);
1380  
    main.invoke(null, args);
1381  
  }
1382  
1383  
  static String invokeJavaCompiler(File optionsFile) throws IOException {
1384  
    String output;
1385  
    if (hasEcj() && !javacOnly)
1386  
      output = invokeEcj(optionsFile);
1387  
    else
1388  
      output = invokeJavac(optionsFile);
1389  
    if (verbose) System.out.println(output);
1390  
    return output;
1391  
  }
1392  
1393  
  private static boolean hasEcj() {
1394  
    try {
1395  
      Class.forName("org.eclipse.jdt.internal.compiler.batch.Main");
1396  
      return true;
1397  
    } catch (ClassNotFoundException e) {
1398  
      return false;
1399  
    }
1400  
  }
1401  
1402  
  private static String invokeJavac(File optionsFile) throws IOException {
1403  
    String output;
1404  
    output = backtick("javac " + bashQuote("@" + optionsFile.getPath()));
1405  
    if (exitValue != 0) {
1406  
      System.out.println(output);
1407  
      throw new RuntimeException("javac returned errors.");
1408  
    }
1409  
    return output;
1410  
  }
1411  
1412  
  // throws ClassNotFoundException if ecj is not in classpath
1413  
  static String invokeEcj(File optionsFile) {
1414  
    try {
1415  
      StringWriter writer = new StringWriter();
1416  
      PrintWriter printWriter = new PrintWriter(writer);
1417  
1418  
      // add more eclipse options in the line below
1419  
1420  
      String[] args = {"@" + optionsFile.getPath(),
1421  
        "-source", javaTarget,
1422  
        "-target", javaTarget,
1423  
        "-nowarn"
1424  
      };
1425  
1426  
      Class ecjClass = Class.forName("org.eclipse.jdt.internal.compiler.batch.Main");
1427  
      Object main = newInstance(ecjClass, printWriter, printWriter, false);
1428  
      call(main, "compile", new Object[]{args});
1429  
      int errors = (Integer) get(main, "globalErrorsCount");
1430  
1431  
      String output = writer.toString();
1432  
      if (errors != 0) {
1433  
        System.out.println(output);
1434  
        throw new RuntimeException("Java compiler returned errors.");
1435  
      }
1436  
      return output;
1437  
    } catch (Exception e) {
1438  
      throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
1439  
    }
1440  
  }
1441  
1442  
  static Object newInstance(Class c, Object... args) { try {
1443  
    Constructor m = findConstructor(c, args);
1444  
    m.setAccessible(true);
1445  
    return m.newInstance(args);
1446  
  } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
1447  
1448  
  static Constructor findConstructor(Class c, Object... args) {
1449  
    for (Constructor m : c.getDeclaredConstructors()) {
1450  
      if (!checkArgs(m.getParameterTypes(), args, verbose))
1451  
        continue;
1452  
      return m;
1453  
    }
1454  
    throw new RuntimeException("Constructor with " + args.length + " matching parameter(s) not found in " + c.getName());
1455  
  }
1456  
1457  
  static boolean checkArgs(Class[] types, Object[] args, boolean debug) {
1458  
    if (types.length != args.length) {
1459  
      if (debug)
1460  
        System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
1461  
      return false;
1462  
    }
1463  
    for (int i = 0; i < types.length; i++)
1464  
      if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
1465  
        if (debug)
1466  
          System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
1467  
        return false;
1468  
      }
1469  
    return true;
1470  
  }
1471  
1472  
  // extended to handle primitive types
1473  
  private static boolean isInstanceX(Class type, Object arg) {
1474  
    if (type == boolean.class) return arg instanceof Boolean;
1475  
    if (type == int.class) return arg instanceof Integer;
1476  
    if (type == long.class) return arg instanceof Long;
1477  
    if (type == float.class) return arg instanceof Float;
1478  
    if (type == short.class) return arg instanceof Short;
1479  
    if (type == char.class) return arg instanceof Character;
1480  
    if (type == byte.class) return arg instanceof Byte;
1481  
    return type.isInstance(arg);
1482  
  }
1483  
1484  
  private static void writeOptions(List<File> sources, List<File> libraries,
1485  
                                   File optionsFile, String moreOptions) throws IOException {
1486  
    FileWriter writer = new FileWriter(optionsFile);
1487  
    for (File source : sources)
1488  
      writer.write(bashQuote(source.getPath()) + " ");
1489  
    if (!libraries.isEmpty()) {
1490  
      List<String> cp = new ArrayList<String>();
1491  
      for (File lib : libraries)
1492  
        cp.add(lib.getAbsolutePath());
1493  
      writer.write("-cp " + bashQuote(join(File.pathSeparator, cp)) + " ");
1494  
    }
1495  
    writer.write(moreOptions);
1496  
    writer.close();
1497  
  }
1498  
1499  
  static void scanForSources(File source, List<File> sources, boolean topLevel) {
1500  
    if (source.isFile() && source.getName().endsWith(".java"))
1501  
      sources.add(source);
1502  
    else if (source.isDirectory() && !isSkippedDirectoryName(source.getName(), topLevel)) {
1503  
      File[] files = source.listFiles();
1504  
      for (File file : files)
1505  
        scanForSources(file, sources, false);
1506  
    }
1507  
  }
1508  
1509  
  private static boolean isSkippedDirectoryName(String name, boolean topLevel) {
1510  
    if (topLevel) return false; // input or output ok as highest directory (intentionally specified by user, not just found by a directory scan in which case we probably don't want it. it's more like heuristics actually.)
1511  
    return name.equalsIgnoreCase("input") || name.equalsIgnoreCase("output");
1512  
  }
1513  
1514  
  static int exitValue;
1515  
  public static String backtick(String cmd) throws IOException {
1516  
    ++processesStarted;
1517  
    File outFile = File.createTempFile("_backtick", "");
1518  
    File scriptFile = File.createTempFile("_backtick", isWindows() ? ".bat" : "");
1519  
1520  
    String command = cmd + " >" + bashQuote(outFile.getPath()) + " 2>&1";
1521  
    //Log.info("[Backtick] " + command);
1522  
    try {
1523  
      saveTextFile(scriptFile.getPath(), command);
1524  
      String[] command2;
1525  
      if (isWindows())
1526  
        command2 = new String[] { scriptFile.getPath() };
1527  
      else
1528  
        command2 = new String[] { "/bin/bash", scriptFile.getPath() };
1529  
      Process process = Runtime.getRuntime().exec(command2);
1530  
      try {
1531  
        process.waitFor();
1532  
      } catch (InterruptedException e) {
1533  
        throw new RuntimeException(e);
1534  
      }
1535  
      exitValue = process.exitValue();
1536  
      if (verbose)
1537  
        System.out.println("Process return code: " + exitValue);
1538  
      return loadTextFile(outFile.getPath(), "");
1539  
    } finally {
1540  
      scriptFile.delete();
1541  
    }
1542  
  }
1543  
1544  
  /** possibly improvable */
1545  
  public static String javaQuote(String text) {
1546  
    return bashQuote(text);
1547  
  }
1548  
1549  
  /** possibly improvable */
1550  
  public static String bashQuote(String text) {
1551  
    if (text == null) return null;
1552  
    return "\"" + text
1553  
      .replace("\\", "\\\\")
1554  
      .replace("\"", "\\\"")
1555  
      .replace("\n", "\\n")
1556  
      .replace("\r", "\\r") + "\"";
1557  
  }
1558  
1559  
  public final static String charsetForTextFiles = "UTF8";
1560  
1561  
  static long TempDirMaker_lastValue;
1562  
1563  
  public static File TempDirMaker_make() {
1564  
    File dir = new File(userHome(), ".javax/" + TempDirMaker_newValue());
1565  
    dir.mkdirs();
1566  
    return dir;
1567  
  }
1568  
1569  
  private static long TempDirMaker_newValue() {
1570  
    long value;
1571  
    do
1572  
      value = System.currentTimeMillis();
1573  
    while (value == TempDirMaker_lastValue);
1574  
    TempDirMaker_lastValue = value;
1575  
    return value;
1576  
  }
1577  
1578  
  !include #1000810 // join
1579  
  
1580  
  public static boolean isWindows() {
1581  
    return System.getProperty("os.name").contains("Windows");
1582  
  }
1583  
1584  
  public static String makeRandomID(int length) {
1585  
    Random random = new Random();
1586  
    char[] id = new char[length];
1587  
    for (int i = 0; i< id.length; i++)
1588  
      id[i] = (char) ((int) 'a' + random.nextInt(26));
1589  
    return new String(id);
1590  
  }
1591  
1592  
  static String computerID;
1593  
  public static String getComputerID() throws IOException {
1594  
    if (noID) return null;
1595  
    if (computerID == null) {
1596  
      File file = new File(userHome(), ".tinybrain/computer-id");
1597  
      computerID = loadTextFile(file.getPath(), null);
1598  
      if (computerID == null) {
1599  
        computerID = makeRandomID(12);
1600  
        saveTextFile(file.getPath(), computerID);
1601  
      }
1602  
      if (verbose)
1603  
        System.out.println("Local computer ID: " + computerID);
1604  
    }
1605  
    return computerID;
1606  
  }
1607  
1608  
  static int fileDeletions;
1609  
1610  
  static void cleanCache() {
1611  
    try {
1612  
      if (verbose)
1613  
        System.out.println("Cleaning cache");
1614  
      fileDeletions = 0;
1615  
      File javax = new File(userHome(), ".javax");
1616  
      long now = System.currentTimeMillis();
1617  
      File[] files = javax.listFiles();
1618  
      if (files != null) for (File dir : files) {
1619  
        if (dir.isDirectory() && Pattern.compile("\\d+").matcher(dir.getName()).matches()) {
1620  
          long time = Long.parseLong(dir.getName());
1621  
          long seconds = (now - time) / 1000;
1622  
          long minutes = seconds / 60;
1623  
          long hours = minutes / 60;
1624  
          if (hours >= tempFileRetentionTime) {
1625  
            //System.out.println("Can delete " + dir.getAbsolutePath() + ", age: " + hours + " h");
1626  
            removeDir(dir);
1627  
          }
1628  
        }
1629  
      }
1630  
      if (verbose && fileDeletions != 0)
1631  
        System.out.println("Cleaned cache. File deletions: " + fileDeletions);
1632  
    } catch (Throwable e) {
1633  
      e.printStackTrace();
1634  
    }
1635  
  }
1636  
1637  
  static void removeDir(File dir) {
1638  
    if (dir.getAbsolutePath().indexOf(".javax") < 0)  // security check!
1639  
      return;
1640  
    for (File f : dir.listFiles()) {
1641  
      if (f.isDirectory())
1642  
        removeDir(f);
1643  
      else {
1644  
        if (verbose)
1645  
          System.out.println("Deleting " + f.getAbsolutePath());
1646  
        f.delete();
1647  
        ++fileDeletions;
1648  
      }
1649  
    }
1650  
    dir.delete();
1651  
  }
1652  
1653  
  static void showSystemProperties() {
1654  
    System.out.println("System properties:\n");
1655  
    for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
1656  
      System.out.println("  " + entry.getKey() + " = " + entry.getValue());
1657  
    }
1658  
    System.out.println();
1659  
  }
1660  
1661  
  static void showVersion() {
1662  
    //showSystemProperties();
1663  
    boolean eclipseFound = hasEcj();
1664  
    //String platform = System.getProperty("java.vendor") + " " + System.getProperty("java.runtime.name") + " " + System.getProperty("java.version");
1665  
    String platform = System.getProperty("java.vm.name") + " " + System.getProperty("java.version");
1666  
    String os = System.getProperty("os.name"), arch = System.getProperty("os.arch");
1667  
    System.out.println("This is " + version + ".");
1668  
    System.out.println("[Details: " +
1669  
      (eclipseFound ? "Eclipse compiler (good)" : "javac (not so good)")
1670  
      + ", " + platform + ", " + arch + ", " + os + "]");
1671  
  }
1672  
1673  
  static boolean isAndroid() {
1674  
    return System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0;
1675  
  }
1676  
  
1677  
  !include #1000415 // set function
1678  
1679  
  static String smartJoin(String[] args) {
1680  
    String[] a2 = new String[args.length];
1681  
    for (int i = 0; i < args.length; i++) {
1682  
      a2[i] = Pattern.compile("\\w+").matcher(args[i]).matches() ? args[i] : quote(args[i]);
1683  
    }
1684  
    return join(" ", a2);
1685  
  }
1686  
  
1687  
  static void logStart(String[] args) throws IOException {
1688  
    String line = smartJoin(args);
1689  
    appendToLog(new File(userHome(), ".javax/log.txt").getPath(), line);
1690  
  }
1691  
  
1692  
  static String quote(String s) {
1693  
    if (s == null) return "null";
1694  
    return "\"" + s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\r", "\\r").replace("\n", "\\n") + "\"";
1695  
  }
1696  
  
1697  
  static void appendToLog(String path, String line) throws IOException {
1698  
    appendToFile(path, "\n" + line + "\n");
1699  
  }
1700  
  
1701  
  static void appendToFile(String path, String s) throws IOException {
1702  
    new File(path).getParentFile().mkdirs();
1703  
    Writer writer = new BufferedWriter(new OutputStreamWriter(
1704  
      new FileOutputStream(path, true), "UTF-8"));
1705  
    writer.write(s);
1706  
    writer.close();
1707  
  }
1708  
1709  
  !include #1000943
1710  
1711  
  static PrintStream oldOut, oldErr;
1712  
  static Thread reader, reader2;
1713  
  static boolean quit; // always false now
1714  
  static PipedInputStream pin=new PipedInputStream();
1715  
  static PipedInputStream pin2=new PipedInputStream();
1716  
  static PipedInputStream pin3=new PipedInputStream();
1717  
1718  
  static class Console extends WindowAdapter implements WindowListener, ActionListener {
1719  
    JFrame frame;
1720  
    JTextArea textArea;
1721  
    JTextField tfInput;
1722  
    StringBuffer buf = new StringBuffer();
1723  
    JButton buttonclear, buttonkill, buttonrestart, buttonduplicate, buttonstacktrace;
1724  
    String[] args;
1725  
1726  
    final DelayedUpdate du = new DelayedUpdate(new Runnable() {
1727  
      public void run() { try {
1728  
1729  
        textArea.append(buf.substring(textArea.getText().length()));
1730  
1731  
      } catch (Exception __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}});
1732  
1733  
    public Console(final String[] args) ctex {
1734  
      this.args = args;
1735  
      // create all components and add them
1736  
      frame=new JFrame(args.length == 0 ? "JavaX Starter Output" : "JavaX Output - " + join(" ", args));
1737  
1738  
		/*Dimension screenSize=Toolkit.getDefaultToolkit().getScreenSize();
1739  
		Dimension frameSize=new Dimension((int)(screenSize.width/2),(int)(screenSize.height/2));
1740  
		int x=(int)(frameSize.width/2);
1741  
		int y=(int)(frameSize.height/2);
1742  
		frame.setBounds(x,y,frameSize.width,frameSize.height);*/
1743  
1744  
      // put in right-bottom corner
1745  
      Rectangle r = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
1746  
      int w = 550, h = 200;
1747  
      frame.setBounds(r.x+r.width-w, r.y+r.height-h, w, h);
1748  
1749  
      textArea=new JTextArea();
1750  
      textArea.setEditable(false);
1751  
      buttonclear = new JButton("clear");
1752  
      buttonkill = new JButton("kill");
1753  
      buttonrestart = new JButton("restart");
1754  
      buttonduplicate = new JButton("duplicate");
1755  
      buttonstacktrace = new JButton("status");
1756  
      buttonstacktrace.setToolTipText("Show threads & stack traces.");
1757  
1758  
      JPanel buttons = new JPanel(new GridLayout(1, 5));
1759  
      buttons.add(buttonclear);
1760  
      buttons.add(buttonkill);
1761  
      buttons.add(buttonrestart);
1762  
      buttons.add(buttonduplicate);
1763  
      buttons.add(buttonstacktrace);
1764  
1765  
      final PipedOutputStream pout3=new PipedOutputStream(pin3);
1766  
      tfInput = new JTextField();
1767  
      tfInput.addActionListener(actionListener {
1768  
        S line = tfInput.getText();
1769  
        try {
1770  
          pout3.write((line + "\n").getBytes("UTF-8"));
1771  
          pout3.flush();
1772  
        } catch (Exception e) {}
1773  
        tfInput.setText("");
1774  
      });
1775  
      
1776  
      JPanel panel = new JPanel(new BorderLayout());
1777  
      panel.add(new JScrollPane(textArea), BorderLayout.CENTER);
1778  
      panel.add(tfInput, BorderLayout.SOUTH);
1779  
      
1780  
      frame.addWindowListener(new WindowAdapter() {
1781  
        public void windowActivated(WindowEvent e) {
1782  
          tfInput.requestFocus();
1783  
        }
1784  
      });
1785  
      
1786  
      frame.getContentPane().setLayout(new BorderLayout());
1787  
      frame.getContentPane().add(panel, BorderLayout.CENTER);
1788  
      frame.getContentPane().add(buttons, BorderLayout.SOUTH);
1789  
      frame.setVisible(true);
1790  
      
1791  
      //frame.addWindowListener(this); // disabled for now
1792  
      buttonclear.addActionListener(this);
1793  
      buttonkill.addActionListener(this);
1794  
      buttonrestart.addActionListener(this);
1795  
      buttonduplicate.addActionListener(this);
1796  
      buttonstacktrace.addActionListener(this);
1797  
1798  
      quit=false; // signals the Threads that they should exit
1799  
      
1800  
      if (args.length != 0) {
1801  
        print("Starting title updater");
1802  
        new Thread("Console Title Updater :)") {
1803  
          public void run() {
1804  
            if (args.length != 0) {
1805  
              int i = 0;
1806  
              while (i < args.length && !isSnippetID(args[i])) ++i;
1807  
              print("Getting title for " + args[i]);
1808  
              String title = getSnippetTitle(args[i]);
1809  
              print("Title: " + title);
1810  
              if (title != null && title.length() != 0)
1811  
                frame.setTitle(title + " [Output]");
1812  
            }
1813  
          }
1814  
        }.start();
1815  
      }
1816  
      
1817  
      System.setIn(pin3);
1818  
      
1819  
    }
1820  
1821  
    public synchronized void windowClosed(WindowEvent evt)
1822  
    {
1823  
      console = null;
1824  
      /*quit=true;
1825  
      this.notifyAll(); // stop all threads
1826  
      try { reader.join(1000);pin.close();   } catch (Exception e){}
1827  
      try { reader2.join(1000);pin2.close(); } catch (Exception e){}
1828  
      System.exit(0);*/
1829  
    }
1830  
1831  
    public synchronized void windowClosing(WindowEvent evt)
1832  
    {
1833  
      frame.setVisible(false); // default behaviour of JFrame
1834  
      frame.dispose();
1835  
    }
1836  
1837  
    public synchronized void actionPerformed(ActionEvent evt) {
1838  
      if (evt.getSource() == buttonkill) {
1839  
        print("Console: Kill button pressed!");
1840  
        // TODO: give threads time to finish, e.g. reportToChat?
1841  
        System.exit(0);
1842  
      } else if (evt.getSource() == buttonrestart) {
1843  
        print("Console: Restart button pressed.");
1844  
        nohupJavax(smartJoin(args));
1845  
        System.exit(0);
1846  
      } else if (evt.getSource() == buttonduplicate) {
1847  
        print("Console: Duplicate button pressed.");
1848  
        nohupJavax(smartJoin(args));
1849  
      } else if (evt.getSource() == buttonstacktrace) {
1850  
        listUserThreadsWithStackTraces();
1851  
      } else {
1852  
        textArea.setText("");
1853  
        buf = new StringBuffer();
1854  
      }
1855  
    }
1856  
1857  
    public void appendText(String s, boolean outNotErr) {
1858  
      //if (verbose) oldOut.println("Console appendText " + outNotErr + " " + quote(s));
1859  
      buf.append(s);
1860  
      du.trigger();
1861  
    }
1862  
  } // Console
1863  
1864  
	static void tryToOpenConsole(String[] args) {
1865  
	  try {
1866  
	    console = new Console(args);
1867  
	  } catch (HeadlessException e) {
1868  
	    // ok, we're headless.
1869  
	  } catch (Throwable e) {
1870  
	    // some other error in console - continue without it
1871  
	    e.printStackTrace();
1872  
	  }
1873  
	}
1874  
1875  
  //// END CONSOLE STUFF
1876  
1877  
  static long now_virtualTime;
1878  
  static long now() {
1879  
    return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis();
1880  
  }
1881  
1882  
  static void print(Object o) {
1883  
    System.out.println(o);
1884  
  }
1885  
1886  
  public synchronized void run()
1887  
  {
1888  
    try
1889  
    {
1890  
      while (Thread.currentThread()==reader)
1891  
      {
1892  
        try { this.wait(100);}catch(InterruptedException ie) {}
1893  
        if (pin.available()!=0)
1894  
        {
1895  
          String input=readLine(pin);
1896  
          //if (verbose) oldOut.println("reader: " + quote(input));
1897  
          appendText(input, true);
1898  
        }
1899  
        if (quit) return;
1900  
      }
1901  
1902  
      while (Thread.currentThread()==reader2)
1903  
      {
1904  
        try { this.wait(100);}catch(InterruptedException ie) {}
1905  
        if (pin2.available()!=0)
1906  
        {
1907  
          String input=readLine(pin2);
1908  
          //if (verbose) oldOut.println("reader2: " + quote(input));
1909  
          appendText(input, false);
1910  
        }
1911  
        if (quit) return;
1912  
      }
1913  
    } catch (Exception e)
1914  
    {
1915  
      appendText("\nConsole reports an Internal error.", false);
1916  
      appendText("The error is: "+e, false);
1917  
    }
1918  
  }
1919  
1920  
  static void redirectSystemOutAndErr() {
1921  
    if (reader != null) return; // did this already
1922  
1923  
    x27 _this = new x27();
1924  
    
1925  
    if (verbose) System.out.println("Redirecting System.out");
1926  
    try
1927  
    {
1928  
      PipedOutputStream pout=new PipedOutputStream(pin);
1929  
      oldOut = System.out;
1930  
      TeeOutputStream tee = new TeeOutputStream(oldOut, pout);
1931  
      System.setOut(new PrintStream(tee,true));
1932  
    }
1933  
    catch (Exception io)
1934  
    {
1935  
      System.err.println("Couldn't redirect STDOUT - " + io.getMessage());
1936  
    }
1937  
1938  
    if (verbose) System.out.println("Redirecting System.err");
1939  
    try
1940  
    {
1941  
      PipedOutputStream pout2=new PipedOutputStream(pin2);
1942  
      oldErr = System.err;
1943  
      TeeOutputStream tee = new TeeOutputStream(oldErr, pout2);
1944  
      System.setErr(new PrintStream(tee,true));
1945  
    }
1946  
    catch (Exception io)
1947  
    {
1948  
      System.err.println("Couldn't redirect STDERR - " + io.getMessage());
1949  
    }
1950  
1951  
    if (verbose) System.out.println("Redirects done. Starting readers");
1952  
    
1953  
    // Starting two seperate threads to read from the PipedInputStreams
1954  
    //
1955  
    reader=new Thread(_this, "StdOut Piper");
1956  
    reader.setDaemon(true);
1957  
    reader.start();
1958  
    //
1959  
    reader2 = new Thread(_this, "StdErr Piper");
1960  
    reader2.setDaemon(true);
1961  
    reader2.start();
1962  
  }
1963  
1964  
  static Appendable customSystemOut;
1965  
1966  
  static void appendText(String s, boolean outNotErr) {
1967  
    // We do this with a TeeOutputStream now (safer).
1968  
    // (outNotErr ? oldOut : oldErr).print(s);
1969  
    
1970  
    if (console != null)
1971  
      console.appendText(s, outNotErr);
1972  
    if (customSystemOut != null)
1973  
      try {
1974  
        customSystemOut.append(s);
1975  
      } catch (IOException e) {
1976  
        e.printStackTrace();
1977  
      }
1978  
  }
1979  
1980  
  static String readLine(PipedInputStream in) throws IOException
1981  
  {
1982  
    String input="";
1983  
    do
1984  
    {
1985  
      int available=in.available();
1986  
      if (available==0) break;
1987  
      byte b[]=new byte[available];
1988  
      in.read(b);
1989  
      input=input+new String(b,0,b.length);
1990  
    }while( !input.endsWith("\n") &&  !input.endsWith("\r\n") && !quit);
1991  
    return input;
1992  
  }
1993  
1994  
  static void nohupJavax(String javaxargs) {
1995  
    try {
1996  
      File xfile = new File(userHome(), ".javax/x27.jar");
1997  
      if (!xfile.isFile()) {
1998  
        String url = "http://tinybrain.de/x27.jar";
1999  
        byte[] data = loadBinaryPage(new URL(url).openConnection());
2000  
        if (data.length < 1000000)
2001  
          throw new RuntimeException("Could not load " + url);
2002  
        saveBinaryFile(xfile.getPath(), data);
2003  
      }
2004  
      String jarPath = xfile.getPath();
2005  
      nohup("java -jar " + (isWindows() ? winQuote(jarPath) : bashQuote(jarPath)) + " " + javaxargs);
2006  
    } catch (Exception e) { throw new RuntimeException(e); }
2007  
  }
2008  
2009  
  /** possibly improvable */
2010  
  public static String winQuote(String text) {
2011  
    if (text == null) return null;
2012  
    return "\"" + text
2013  
      .replace("\\", "\\\\")
2014  
      .replace("\"", "\\\"")
2015  
      .replace("\n", "\\n")
2016  
      .replace("\r", "\\r") + "\"";
2017  
  }
2018  
2019  
  public static File nohup(String cmd) throws IOException {
2020  
    File outFile = File.createTempFile("nohup_" + nohup_sanitize(cmd), ".out");
2021  
    nohup(cmd, outFile, false);
2022  
    return outFile;
2023  
  }
2024  
2025  
  static String nohup_sanitize(String s) {
2026  
    return s.replaceAll("[^a-zA-Z0-9\\-_]", "");
2027  
  }
2028  
2029  
  /** outFile takes stdout and stderr. */
2030  
  public static void nohup(String cmd, File outFile, boolean append) throws IOException {
2031  
    String command = nohup_makeNohupCommand(cmd, outFile, append);
2032  
2033  
    File scriptFile = File.createTempFile("_realnohup", isWindows() ? ".bat" : "");
2034  
    System.out.println("[Nohup] " + command);
2035  
    try {
2036  
      //System.out.println("[RealNohup] Script file: " + scriptFile.getPath());
2037  
      saveTextFile(scriptFile.getPath(), command);
2038  
      String[] command2;
2039  
      if (isWindows())
2040  
        command2 = new String[] {"cmd", "/c", "start", "/b", scriptFile.getPath() };
2041  
      else
2042  
        command2 = new String[] {"/bin/bash", scriptFile.getPath() };
2043  
2044  
      Process process = Runtime.getRuntime().exec(command2);
2045  
      try {
2046  
        process.waitFor();
2047  
      } catch (InterruptedException e) {
2048  
        throw new RuntimeException(e);
2049  
      }
2050  
      int value = process.exitValue();
2051  
      //System.out.println("exit value: " + value);
2052  
    } finally {
2053  
      if (!isWindows())
2054  
        scriptFile.delete();
2055  
    }
2056  
  }
2057  
2058  
  public static String nohup_makeNohupCommand(String cmd, File outFile, boolean append) {
2059  
    mkdirsForFile(outFile);
2060  
2061  
    String command;
2062  
    if (isWindows())
2063  
      command = cmd + (append ? " >>" : " >") + winQuote(outFile.getPath()) + " 2>&1";
2064  
    else
2065  
      command = "nohup " + cmd + (append ? " >>" : " >") + bashQuote(outFile.getPath()) + " 2>&1 &";
2066  
    return command;
2067  
  }
2068  
2069  
  public static void mkdirsForFile(File file) {
2070  
    File dir = file.getParentFile();
2071  
    if (dir != null) // is null if file is in current dir
2072  
      dir.mkdirs();
2073  
  }
2074  
 
2075  
  static void autoReportToChat() {
2076  
    if (customSystemOut == null) {
2077  
      print("Auto-reporting to chat.");
2078  
      customSystemOut = new Appendable() {
2079  
        LineBuf buf = new LineBuf();
2080  
        
2081  
        // only using this one
2082  
        public Appendable append(CharSequence cs) {
2083  
          buf.append(cs.toString());
2084  
          while (true) {
2085  
            String s = buf.nextLine();
2086  
            if (s == null) break;
2087  
            reportToChat(s, true);
2088  
          }
2089  
          return this;
2090  
        }
2091  
    
2092  
        public Appendable append(char c) { return this; }
2093  
        public Appendable append(CharSequence s, int start, int end) { return this; }
2094  
      };
2095  
     }
2096  
  }
2097  
  
2098  
  static class Q {
2099  
    LinkedBlockingQueue<Runnable> q = new LinkedBlockingQueue<Runnable>();
2100  
    
2101  
    static class Done extends RuntimeException {}
2102  
    
2103  
    Q() {}
2104  
    
2105  
    Q(String name, boolean startThread) {
2106  
      if (startThread)
2107  
        new Thread(name) {
2108  
          public void run() {
2109  
            Q.this.run();
2110  
          }
2111  
        }.start();
2112  
    }
2113  
    
2114  
    Iterable<Runnable> master() {
2115  
      return new Iterable<Runnable>() {
2116  
        public Iterator<Runnable> iterator() {
2117  
          return new Iterator<Runnable>() {
2118  
            Runnable x;
2119  
            
2120  
            public boolean hasNext() { try {
2121  
   
2122  
              //debug("hasNext");
2123  
              while (x == null)
2124  
                x = q.poll(1, TimeUnit.DAYS);
2125  
              //debug("hasNext true");
2126  
              return true;
2127  
            
2128  
  } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2129  
            
2130  
            public Runnable next() {
2131  
              //debug("next");
2132  
              hasNext();
2133  
              Runnable _x = x;
2134  
              x = null;
2135  
              //debug("next " + structure(x));
2136  
              return _x;
2137  
            }
2138  
            
2139  
            public void remove() {
2140  
            }
2141  
          };
2142  
        }
2143  
      };
2144  
    }
2145  
    
2146  
    void add(Runnable r) {
2147  
      q.add(r);
2148  
    }
2149  
    
2150  
    void run() {
2151  
      for (Runnable r : master()) {
2152  
        try {
2153  
          r.run();
2154  
        } catch (Done e) {
2155  
          return; // break signal
2156  
        } catch (Throwable e) {
2157  
          e.printStackTrace();
2158  
        }
2159  
      }
2160  
    }
2161  
    
2162  
    void done() {
2163  
      add(new Runnable() {
2164  
        public void run() {
2165  
          throw new Done();
2166  
        }
2167  
      });
2168  
    }
2169  
  } // class Q
2170  
2171  
static void reportToChat(final String s, boolean silent) {
2172  
    if (s == null || s.length() == 0) return;
2173  
    if (!silent)
2174  
      print("reportToChat: " + quote(s));
2175  
    reportToChat_getChatThread().add(new Runnable() {
2176  
    public void run() { try {
2177  
        startChatServerIfNotUp();
2178  
        waitForChatServer();
2179  
        chatSend(s);
2180  
    } catch (Exception __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}});
2181  
   }
2182  
  
2183  
  static Q reportToChat_q;
2184  
  
2185  
  static Q reportToChat_getChatThread() {
2186  
    if (reportToChat_q == null)
2187  
      reportToChat_q = new Q("reportToChat", true);
2188  
    return reportToChat_q;
2189  
  }
2190  
  
2191  
  static void startChatServerIfNotUp() {
2192  
    if (portIsBound(9751)) {
2193  
      //print("Chat seems to be up.");
2194  
    } else {
2195  
      nohupJavax("1000867");
2196  
      print("Chat server should be coming up any minute now.");
2197  
    }
2198  
  }
2199  
  
2200  
  static void waitForChatServer() {
2201  
    if (!portIsBound(9751)) {
2202  
      //System.out.print("Waiting for chat server... ");
2203  
      do {
2204  
        sleep(1000);
2205  
      } while (!portIsBound(9751));
2206  
      //print("OK.");
2207  
    }
2208  
  }
2209  
2210  
  static boolean portIsBound(int port) {
2211  
    try {
2212  
      ServerSocket s = new ServerSocket(port);
2213  
      s.close();
2214  
      return false;
2215  
    } catch (IOException e) {
2216  
      return true;
2217  
    }
2218  
  }
2219  
  
2220  
  static class LineBuf {
2221  
    StringBuffer buf = new StringBuffer();
2222  
    
2223  
    void append(String s) {
2224  
      buf.append(s);
2225  
    }
2226  
    
2227  
    String nextLine() {
2228  
      int i = buf.indexOf("\n");
2229  
      if (i >= 0) {
2230  
        String s = buf.substring(0, i > 0 && buf.charAt(i-1) == '\r' ? i-1 : i);
2231  
        buf.delete(0, i+1);
2232  
        return s;
2233  
      }
2234  
      return null;
2235  
    }
2236  
  } // LineBuf
2237  
2238  
  static void sleep(long ms) {
2239  
    try {
2240  
      Thread.sleep(ms);
2241  
    } catch (Exception e) { throw new RuntimeException(e); }
2242  
  }
2243  
2244  
  !include #1000937 // chatSend
2245  
2246  
  static class TeeOutputStream extends OutputStream {
2247  
    
2248  
    protected OutputStream out, branch;
2249  
2250  
    public TeeOutputStream( OutputStream out, OutputStream branch ) {
2251  
      this.out = out;
2252  
      this.branch = branch;
2253  
    }
2254  
2255  
    @Override
2256  
    public synchronized void write(byte[] b) throws IOException {
2257  
      write(b, 0, b.length);
2258  
    }
2259  
2260  
    @Override
2261  
    public synchronized void write(byte[] b, int off, int len) throws IOException {
2262  
      //if (verbose) oldOut.println("Tee write " + new String(b, "UTF-8"));
2263  
      out.write(b, off, len);
2264  
      this.branch.write(b, off, len);
2265  
    }
2266  
2267  
    @Override
2268  
    public synchronized void write(int b) throws IOException {
2269  
      write(new byte[] {(byte) b});
2270  
    }
2271  
2272  
    /**
2273  
     * Flushes both streams.
2274  
     * @throws IOException if an I/O error occurs
2275  
     */
2276  
    @Override
2277  
    public void flush() throws IOException {
2278  
      out.flush();
2279  
      this.branch.flush();
2280  
    }
2281  
2282  
    /**
2283  
     * Closes both streams.
2284  
     * @throws IOException if an I/O error occurs
2285  
     */
2286  
    @Override
2287  
    public void close() throws IOException {
2288  
      out.close();
2289  
      this.branch.close();
2290  
    }
2291  
  }
2292  
  
2293  
  static boolean isChatServer(String[] args) {
2294  
    for (int i = 0; i < args.length; i++)
2295  
      if (isSnippetID(args[i]))
2296  
        return parseSnippetID(args[i]) == 1000867;
2297  
    return false;
2298  
  }
2299  
  
2300  
  static String getSnippetTitle(String id) {
2301  
    try {
2302  
      return loadPage(new URL("http://tinybrain.de:8080/tb-int/getfield.php?id=" + parseSnippetID(id) + "&field=title"));
2303  
    } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }
2304  
  }
2305  
  
2306  
  static void listUserThreadsWithStackTraces() {
2307  
    print("");
2308  
    Map<Thread, StackTraceElement[]> threadMap = Thread.getAllStackTraces();
2309  
    int n = 0;
2310  
    for (Thread t : threadMap.keySet()) {
2311  
      ThreadGroup g = t.getThreadGroup();
2312  
      if (g != null && g.getName().equals("system")) continue;
2313  
      ++n;
2314  
      print(t);
2315  
      for (StackTraceElement e : threadMap.get(t)) {
2316  
        print("  " + e);
2317  
      }
2318  
      print("");
2319  
    }
2320  
    print(n + " user threads.");
2321  
  }
2322  
  
2323  
  static void killMyself() {
2324  
    print("Killing myself. (insert overall feeling here)");
2325  
    System.exit(0);
2326  
  }
2327  
  
2328  
  static void makeVMAndroid() {
2329  
    makeAndroidNoConsoleDaemon("This is a JavaX VM.", stringfunc { answer(s) });
2330  
  }
2331  
  
2332  
  static class Info {
2333  
    S programID;
2334  
    S[] programArgs;
2335  
    File transpiledSrc;
2336  
    Class mainClass;
2337  
  }
2338  
  
2339  
  static new Info info;
2340  
  
2341  
  static synchronized S answer(S s) {
2342  
    new Matches m;
2343  
    
2344  
    if (match3("kill!", s)) {
2345  
      killMyself();
2346  
      return "ok";
2347  
    }
2348  
    if (match3("What is your process ID?", s) || match3("what is your pid?", s))
2349  
      return getPID();
2350  
    if (match3("what is your program id?", s))
2351  
      return info.programID;
2352  
    if (match3("what are your program arguments?", s))
2353  
      return structure(info.programArgs);
2354  
    if (match3("get fields of main class", s))
2355  
      return structure(listFields(info.mainClass));
2356  
    if (match3("get field * of main class", s, m))
2357  
      return structure(get(info.mainClass, m.m[0]));
2358  
    if (match3("invoke function * of main class", s, m))
2359  
      return structure(call(info.mainClass, m.m[0]));
2360  
    if (match3("set field * of main class to *", s, m)) {
2361  
      set(info.mainClass, m.m[0], unstructure(m.m[1]));
2362  
      return "ok";
2363  
    }
2364  
    if (match3("how much memory are you consuming", s))
2365  
      return "Java heap size: " + (Runtime.getRuntime().totalMemory()+1024*1024-1)/1024/1024 + " MB";
2366  
    if (match3("how much memory is used after GC?", s)) {
2367  
      System.gc();
2368  
      return "Java heap used: " + (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()+1024*1024-1)/1024/1024 + " MB";
2369  
    }
2370  
    if (match3("how much memory is used?", s))
2371  
      return "Java heap used: " + (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()+1024*1024-1)/1024/1024 + " MB";
2372  
    return null;
2373  
  }
2374  
  
2375  
  !include #1001103 // makeAndroid / makeAndroidNoConsole
2376  
  !include #1001074 // readLine
2377  
  !include #1000891 // setOpt
2378  
  !include #1000619 // get
2379  
  !include #1001152 // getMainClass
2380  
}

Author comment

Began life as a copy of #1001110

download  show line numbers  debug dex  old transpilations   

Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1001148
Snippet name: x27.java (JavaX)
Eternal ID of this version: #1001148/1
Text MD5: 78db784b1c3a5c697d090fa663c6fd8b
Transpilation MD5: 9362723143a4e0f5cff7c9969000c38e
Author: stefan
Category: javax
Type: JavaX source code
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2015-09-25 18:10:21
Source code size: 83042 bytes / 2380 lines
Pitched / IR pitched: No / Yes
Views / Downloads: 713 / 635
Referenced in: [show references]