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

2976
LINES

< > BotCompany Repo | #1001638 // x30.java (JavaX) - packaged by #1001639

JavaX module (desktop) [tags: use-pretranspiled]

Download Jar. Libraryless. Click here for Pure Java version (29459L/172K).

1  
!7
2  
3  
// See also #1016582 for the Global Utils
4  
5  
set flag AllPublic.
6  
set flag InCore.
7  
// mainClassName x30 // would be good to support "main <id>" but causes a problem
8  
9  
should not include function cget.
10  
do not include function has.
11  
do not include function list.
12  
should not include class RemoteDB.
13  
should not include class Concepts.
14  
15  
/**
16  
 JavaX runner version 30
17  
18  
 Changes to v29:
19  
 -added integrated MultiPort
20  
 -useBossBot = false
21  
 -Beginning support for offline mode (transpiled programs only so far)
22  
 -faster console update (100ms)
23  
 -weakref
24  
 -made first program an injection
25  
 -manage multi-ports
26  
 -multitry for loadPage (partially implemented)
27  
 -list injections
28  
 -additional "get output" questions
29  
 -send line * to system.in
30  
 -multi-port 1 accessible directly through VM android
31  
 -getInjectionID (from main.class)
32  
 -allow programs without main method
33  
 -increase buffer size for PipedInputStream (console output/input)
34  
 -registerSourceCode + getSourceCodeForClass
35  
 -autoScroll
36  
 -retaining class files for 30 days (restart JavaX programs after 30 days!!)
37  
 -close confirmation for console (no running apps without console!)
38  
 -utf8 fixed in transpilations!
39  
 -maxConsoleChars
40  
 -pause button
41  
 -regular GC every 60s
42  
 -GC after main ran
43  
 -and more
44  
 
45  
 TODO when making x31: change program ID in source
46  
 */
47  
48  
interface StringFunc {
49  
  String get(String s);
50  
}
51  
52  
/*interface DynamicMethods {
53  
  O _dynCall(S methodName, O[] args);
54  
}*/
55  
56  
m { p { throw new RuntimeException("placebo"); } }
57  
58  
class x30 {
59  
  static final String version = "JavaX 30";
60  
  static final int subversion = 2;
61  
  static S dateChanged = "2020/06/19";
62  
  static final S javaxProgramID = "#1001638";
63  
  
64  
  // If programs run longer than this, they might have their class files
65  
  // deleted. Should set to 30 for servers. Working on real solution.
66  
  static int tempFileRetentionTime = 2*24; // hours
67  
  
68  
  static int maxConsoleChars = 1024*1024;
69  
  static int consoleUpdateDelay = 50; // ms
70  
  static int pipeDelay = 50;
71  
  static bool focusConsoleInputOnActivate = false;
72  
  sS donePrefix = "."; // "[Done] ";
73  
  static bool consoleGrabFocus;
74  
  
75  
  static int consoleWidth = 550, consoleHeight = 200;
76  
  static int consoleXGap = 10, consoleYGap = 10;
77  
  
78  
  static bool gcAfterMainDone = true, registerSourceCodes = false;
79  
  static bool verbose = false, translate = false, list = false, virtualizeTranslators = true;
80  
  sbool dontCompileThroughPort5000;
81  
  static S translateTo = null;
82  
  static bool noID = false, noPrefetch = false, noAWT = false;
83  
  static bool safeOnly = false, safeTranslate = false, javacOnly = false, logOn = true, noCleanCache;
84  
  static volatile bool cleanCacheDone;
85  
  static bool runMainInProcess = true, consoleOn = true, hasHelloMessage = false;
86  
  static L<S[]> mainTranslators = new ArrayList<S[]>();
87  
  private static Map<Long, String> memSnippetCache = new HashMap<Long, String>();
88  
  private static int processesStarted, compilations;
89  
90  
  // snippet ID -> md5
91  
  private static HashMap<Long, String> prefetched = new HashMap<Long, String>();
92  
  private static File virtCache;
93  
  
94  
  // doesn't work yet
95  
  private static Map<String, Class<?>> programCache = new HashMap<String, Class<?>>();
96  
  static boolean cacheTranslators = false;
97  
98  
  // this should work (caches transpiled translators)
99  
  private static HashMap<Long, Object[]> translationCache = new HashMap<Long, Object[]>();
100  
  static boolean cacheTranspiledTranslators = true;
101  
102  
  // which snippets are available pre-transpiled server-side?
103  
  private static Set<Long> hasTranspiledSet = new HashSet<Long>();
104  
  static boolean useServerTranspiled = true;
105  
  
106  
  static boolean useBossBot = false;
107  
108  
  static Object androidContext;
109  
  static boolean android = isAndroid();
110  
  
111  
  // We used to stick to 1.7 to support Android.
112  
  // Now using Java 11.
113  
  static S javaTarget =
114  
      System.getProperty("java.version").startsWith("1.6.") ? "1.6" 
115  
    : "11";
116  
117  
  // Translators currently being translated (to detect recursions)
118  
  private static Set<Long> translating = new HashSet<Long>();
119  
120  
  static String lastOutput;
121  
  static String[] fullArgs;
122  
  private static Console console;
123  
  
124  
  static String javaCompilerOutput;
125  
  
126  
  static int systemOutPipeSize = 128*1024; // 128 K
127  
  static int systemErrPipeSize = 4*1024; // 4 K
128  
  static int systemInPipeSize = 4*1024; // 4 K
129  
  
130  
  static S caseID;
131  
  static volatile bool hiddenVM;
132  
  
133  
  static long vmStarted, vmStarted_sysTime;
134  
  
135  
  // store anything here
136  
  static Map generalMap = synchroHashMap();
137  
  
138  
  static {
139  
    //putIfNotThere(generalMap, newPing_actionTL := new BetterThreadLocal);
140  
    vmStarted = now();
141  
    vmStarted_sysTime = sysNow();
142  
  }
143  
  
144  
  sclass VMKeep {
145  
    S programMD5;
146  
    new HashMap<S, S> vars; // var name -> structure
147  
  }
148  
  
149  
  static new HashMap<S, VMKeep> vmKeep;
150  
  static Lock vmKeep_lock = lock();
151  
  
152  
  public static void main(String[] args) {
153  
    try {
154  
      goMain(args);
155  
    } catch (Throwable e) {
156  
      printStackTrace(e);
157  
    }
158  
  }
159  
  
160  
  // rewrite command line args if we're called from browser
161  
  // e.g. "javax:123"
162  
  static S[] rewriteArgsForURLHandler(S[] args) {
163  
    //print("Args: " + sfu(args));
164  
    if (l(args) == 1 && args[0].startsWith("javax:")) {
165  
      S a = args[0];
166  
      //print("Rewriting argument: " + a);
167  
      a = dropPrefix("javax:", a);
168  
      a = trim(dropPrefix("//", a));
169  
      a = dropSuffix("/", a);
170  
      ret asStringArray(splitAtSpace(a)); // TODO: handle quotes
171  
    }
172  
    ret args;
173  
  }
174  
  
175  
  sbool inited;
176  
  
177  
  svoid initMe {
178  
    if (inited) ret;
179  
    inited = true;
180  
    loadAllClasses();
181  
    coreInit();
182  
    regularGC();
183  
    computerID(); // Generate!
184  
  }
185  
  
186  
  svoid coreInit {
187  
    __javax = x30.class; // for hotwire
188  
    // init Global Util's reference to me
189  
    Class x30_util = classForName("x30_pkg.x30_util");
190  
    setOpt(x30_util, "__javax", x30.class);
191  
    callOnLoadMethods(x30_util);
192  
  }
193  
  
194  
  svoid initHelloMessage {
195  
    if (!hasHelloMessage) {
196  
      hasHelloMessage = true;
197  
      //installHelloMessage(args.length == 0 ? "JavaX Start-Up VM" : "JavaX VM (" + smartJoin(args) + ")");
198  
      makeVMAndroid();
199  
    }
200  
  }
201  
  
202  
  static void goMain(String[] args) throws Exception {
203  
    initMe();
204  
    
205  
    //appendToMechQ_withDate("x30 start log on computer " + computerID(), quote(first(args)));
206  
    
207  
    args = rewriteArgsForURLHandler(args);
208  
    
209  
    if (args.length != 0 && args[0].equals("-v")) verbose = true;
210  
    redirectSystemOutAndErr();
211  
212  
    for (S arg : args)
213  
      if (arg.equals("-noawt"))
214  
        noAWT = true;
215  
        
216  
    if (consoleOn && console == null && !noAWT)
217  
      tryToOpenConsole(args);
218  
    
219  
    String autoReport = loadTextFile(new File(userHome(), ".javax/auto-report-to-chat").getPath(), "").trim();
220  
    //print("autoReport=" + autoReport);
221  
    if (!isChatServer(args) && autoReport.equals("1"))
222  
      autoReportToChat();
223  
224  
    initHelloMessage();
225  
    
226  
    File ioBaseDir = new File("."), inputDir = null, outputDir = null;
227  
    String src = null;
228  
    List<String> programArgs = new ArrayList<String>();
229  
    fullArgs = args;
230  
231  
    for (int i = 0; i < args.length; i++) {
232  
      String arg = args[i];
233  
      
234  
      // --quiet to -quiet
235  
      arg = regexpReplace_direct(arg, "^--(?=\\w)", "-");
236  
237  
      if (arg.equals("-version")) {
238  
        showVersion();
239  
        System.exit(0);
240  
      }
241  
242  
      if (arg.equals("-sysprop")) {
243  
        showSystemProperties();
244  
        return;
245  
      }
246  
      
247  
      if (arg.equals("-v") || arg.equals("-verbose"))
248  
        verbose = true;
249  
      else if (arg.equals("-nocerts"))
250  
        disableCertificateValidation();
251  
      else if (arg.equals("-finderror"))
252  
        verbose = true;
253  
      else if (arg.equals("-offline") || arg.equalsIgnoreCase("-prefercached"))
254  
        preferCached = true;
255  
      else if (arg.equals("-novirt"))
256  
        virtualizeTranslators = false;
257  
      else if (arg.equals("-safeonly"))
258  
        safeOnly = true;
259  
      else if (arg.equals("-safetranslate"))
260  
        safeTranslate = true;
261  
      else if (arg.equals("-noawt"))
262  
        noAWT = true;
263  
      else if (arg.equals("-quiet"))
264  
        quietLoading = true;
265  
      else if (arg.equals("-verbose-illegal"))
266  
        verboseIllegal = true;
267  
      else if (arg.equals("-case"))
268  
        caseID = args[++i];
269  
      else if (arg.equals("-noid"))
270  
        noID = true;
271  
      else if (arg.equals("-nocachetranspiled"))
272  
        cacheTranspiledTranslators = false;
273  
      else if (arg.equals("-javac"))
274  
        javacOnly = true;
275  
      else if (arg.equals("-nocleancache"))
276  
        noCleanCache = true;
277  
      else if (arg.equals("-localtranspile"))
278  
        useServerTranspiled = false;
279  
      else if (arg.equals("translate") && src == null)
280  
        translate = true;
281  
      else if (arg.equals("eval") && src == null)
282  
        src = #1006008;
283  
      else if (arg.equals("list") && src == null) {
284  
        list = true;
285  
        virtualizeTranslators = false; // so they are silenced
286  
      } else if (arg.equals("run") && src == null) {
287  
        // it's the default command anyway
288  
      } else if (arg.startsWith("input="))
289  
        inputDir = new File(arg.substring(6));
290  
      else if (arg.startsWith("output="))
291  
        outputDir = new File(arg.substring(7));
292  
      else if (arg.equals("with"))
293  
        mainTranslators.add(new String[] {args[++i], null});
294  
      else if (translate && arg.equals("to"))
295  
        translateTo = args[++i];
296  
      else if (eqic(arg, "-dontcompilethroughport5000"))
297  
        dontCompileThroughPort5000 = true;
298  
      else if (eqic(arg, "-livecode")) {
299  
        vm_setResourceLoader(resourceLoaderFromLiveCodeDirWithInitializing());
300  
        if (verbose) print("Using " + liveCodeDir() + " with on-demand downloads from " + tb_mainServer());
301  
      }
302  
303  
      // add more options here
304  
305  
      else if (src == null) {
306  
        //System.out.println("src=" + arg);
307  
        src = arg;
308  
      } else
309  
        programArgs.add(arg);
310  
    }
311  
312  
    if (!noCleanCache) thread "Clean Cache" { cleanCache(); }
313  
314  
    if (useServerTranspiled)
315  
      noPrefetch = true;
316  
317  
    if (src == null && fileExists("main.java")) {
318  
      src = ".";
319  
      print("WARNING: Runnning " + absolutePath("main.java") + " - this may not be what you want. Change to another directory to resume normal operation.");
320  
    }
321  
322  
    // Might actually want to write to 2 disk caches (global/per program).
323  
    if (virtualizeTranslators && !preferCached)
324  
      virtCache = TempDirMaker_make();
325  
326  
    if (inputDir != null) {
327  
      ioBaseDir = TempDirMaker_make();
328  
      System.out.println("Taking input from: " + inputDir.getAbsolutePath());
329  
      System.out.println("Output is in: " + new File(ioBaseDir, "output").getAbsolutePath());
330  
      copyInput(inputDir, new File(ioBaseDir, "input"));
331  
    }
332  
    
333  
    if (logOn)
334  
      logStart(args);
335  
      
336  
    temp quietLoading ? temp_loadPage_silent() : null;
337  
    if (verboseIllegal) {
338  
      print("verboseIllegal ON");
339  
      vmBus_onMessage makeAccessible_error(voidfunc(Throwable e, O o) {
340  
        System.out.print(e);
341  
      });
342  
    }
343  
344  
    javaxmain(src, ioBaseDir, translate, list, programArgs.toArray(new String[programArgs.size()]));
345  
346  
    if (outputDir != null) {
347  
      copyInput(new File(ioBaseDir, "output"), outputDir);
348  
      System.out.println("Output copied to: " + outputDir.getAbsolutePath());
349  
    }
350  
351  
    if (verbose) {
352  
      // print stats
353  
      print("Processes started: " + processesStarted + ", compilations: " + compilations);
354  
      print("Timers: " + l(_registeredTimers()));
355  
    }
356  
  }
357  
358  
  public static void javaxmain(String src, File ioDir, boolean translate, boolean list,
359  
                               String[] args) throws Exception {
360  
    String programID = isSnippetID(src) ? "" + parseSnippetID(src) : null;
361  
    
362  
    if (programID != null && !quietLoading)
363  
      System.out.println(trim("JavaX TRANSLATE " + programID + " " + smartJoin(args)) + " / " + vmPort());
364  
    
365  
    new L<File> libraries;
366  
    new LS libraries2;
367  
    File x = transpileMain(src, libraries, libraries2);
368  
    if (verbose)
369  
      print("After transpileMain: " + x);
370  
      
371  
    if (x == null) {
372  
      showVersion();
373  
      
374  
      // DEFAULT PROGRAM TO RUN
375  
      
376  
      if (fullArgs != null) {
377  
        String[] nargs;
378  
        if (fullArgs.length == 0) {
379  
          nargs = new String[] {str(psI(javaxDefaultProgram()))};
380  
          loadPage_retries = 60*60; // try to get internet for an hour instead of a minute, lol
381  
        } else {
382  
          // forward to search
383  
          nargs = new String[fullArgs.length+1];
384  
          nargs[0] = str(psI(#636));
385  
          // nargs[1] = "search-runnables";
386  
          System.arraycopy(fullArgs, 0, nargs, 1, fullArgs.length);
387  
        }
388  
        if (console != null)
389  
          console.args = nargs;
390  
        main(nargs); // Hopefully we get no infinite recursion :)
391  
        return;
392  
      }
393  
      
394  
      System.out.println("No main.java found, exiting");
395  
      return;
396  
    }
397  
    
398  
    info.transpiledSrc = x;
399  
400  
    // list or run
401  
402  
    if (translate) {
403  
      File to = x;
404  
      if (translateTo != null) {
405  
        StringBuilder buf = new StringBuilder();
406  
        // XXX for (File f : libraries) buf.append(f.getName() + "\n");
407  
        for (S s : libraries2) buf.append("data_" + s + ".jar\n");
408  
        if (new File(translateTo).isDirectory()) {
409  
          to = new File(translateTo, "main.java");
410  
          saveTextFile(new File(translateTo, "libraries.txt").getPath(), buf.toString());
411  
        } else {
412  
          to = new File(translateTo);
413  
          saveTextFile(new File(translateTo + "_libraries").getPath(), buf.toString());
414  
        }
415  
      }
416  
      if (to != x)
417  
        copy(new File(x, "main.java"), to);
418  
      System.out.println("Program translated to: " + to.getAbsolutePath());
419  
    } else if (list)
420  
      System.out.println(loadTextFile(new File(x, "main.java").getPath(), null));
421  
    else {
422  
      if (!quietLoading) {
423  
        if (programID != null)
424  
          System.out.println("JavaX RUN " + programID + " " + smartJoin(args));
425  
        System.out.println(); // Make empty line before actual program starts
426  
      }
427  
      
428  
      PaA paa = javax2(x, ioDir, false, runMainInProcess, libraries, args, null, programID, info);
429  
      
430  
      final bool error = paa != null && paa.exception != null;
431  
      if (error ||
432  
        !(quietLoading || !isTrue(getOpt(paa.mainClass, 'mainDoneQuietly_on))))
433  
        System.out.println("\n" + (error ? "[main done with error]" : "[main done]"));
434  
      if (console != null) awt {
435  
        if (eq(console.frame.getTitle(), console.title + " [Output]") && autoVMExit_visibleObjects() <= 1)
436  
          console.frame.setTitle((error ? "[Error] " : donePrefix) + console.title);
437  
      }
438  
      
439  
      // cleanup reportToChat thread
440  
      if (reportToChat_q != null) {
441  
        if (customSystemOut != null)
442  
          Thread.sleep(1000); // delay to finish autoReportToChat. Yes it's hacky.
443  
        if (verbose) System.out.println("Closing reportToChat queue");
444  
        reportToChat_q.done();
445  
      }
446  
      
447  
      if (gcAfterMainDone) gc();
448  
    }
449  
  }
450  
451  
  // called by clients somewhere
452  
  static File transpileMain(String src, List<File> libraries) throws Exception {
453  
    ret transpileMain(src, libraries, new L);
454  
  }
455  
  
456  
  static File transpileMain(String src, List<File> libraries, LS libraries2) throws Exception {
457  
    File srcDir = null;
458  
    boolean isTranspiled = false;
459  
    if (verbose) printVars_str transpileMain(+src, +libraries, +libraries2);
460  
    if (isSnippetID(src)) {
461  
      S transpiledSrc = useBossBot ? getTranspilationFromBossBot(parseSnippetID(src)) : null;
462  
      if (transpiledSrc != null) {
463  
        int i = transpiledSrc.indexOf('\n');
464  
        String libs = transpiledSrc.substring(0, Math.max(0, i));
465  
        //print("libs for " + src + ": " + libs);
466  
        transpiledSrc = transpiledSrc.substring(i+1);
467  
        if (!transpiledSrc.isEmpty()) {
468  
          srcDir = TempDirMaker_make();
469  
          saveTextFile(new File(srcDir, "main.java").getPath(), transpiledSrc);
470  
          isTranspiled = true;
471  
472  
          Matcher m = Pattern.compile("\\d+").matcher(libs);
473  
          while (m.find()) {
474  
            String libid = m.group();
475  
            File libraryFile = DiskSnippetCache_getLibrary(parseSnippetID(libid));
476  
            loadLib(libid, libraries, libraries2, libraryFile);
477  
          }
478  
        }
479  
      }
480  
     
481  
      if (srcDir == null) {
482  
        prefetch(src);
483  
        long id = parseSnippetID(src);
484  
        prefetched.remove(id); // hackfix to ensure transpiled main program is found.
485  
        bool offline = isOfflineMode();
486  
        srcDir = offline ? null : loadSnippetAsMainJava(src);
487  
        if (verbose)
488  
          System.err.println("hasTranspiledSet: " + hasTranspiledSet);
489  
        if (hasTranspiledSet.contains(id) && useServerTranspiled || offline) {
490  
          //System.err.println("Trying pretranspiled main program: #" + id);
491  
          transpiledSrc = getServerTranspiled2("#" + id);
492  
          int i = transpiledSrc.indexOf('\n');
493  
          String libs = transpiledSrc.substring(0, Math.max(0, i));
494  
          if (verbose) print("libs for " + src + ": " + libs);
495  
          transpiledSrc = transpiledSrc.substring(i+1);
496  
          if (!transpiledSrc.isEmpty()) {
497  
            srcDir = TempDirMaker_make();
498  
            saveTextFile(new File(srcDir, "main.java").getPath(), transpiledSrc);
499  
            isTranspiled = true;
500  
            //translationCache.put(id, new Object[] {srcDir, libraries});
501  
  
502  
            Matcher m = Pattern.compile("\\d+").matcher(libs);
503  
            while (m.find()) {
504  
              String libid = m.group();
505  
              File libraryFile = DiskSnippetCache_getLibrary(parseSnippetID(libid));
506  
              loadLib(libid, libraries, libraries2, libraryFile);
507  
              if (verbose) print("libid=" + libid + ", libraryFile: " + libraryFile + ", libraries2=" + libraries2);
508  
            }
509  
          }
510  
        }
511  
      }
512  
    } else {
513  
      if (src == null) null;
514  
      srcDir = new File(src);
515  
516  
      // if the argument is a file, it is assumed to be main.java
517  
      if (srcDir.isFile()) {
518  
        srcDir = TempDirMaker_make();
519  
        copy(new File(src), new File(srcDir, "main.java"));
520  
      }
521  
522  
      if (!new File(srcDir, "main.java").exists())
523  
        return null;
524  
    }
525  
526  
    // translate
527  
528  
    File x = srcDir;
529  
530  
    if (!isTranspiled) {
531  
      temp tempSetThreadLocal(transpilingSnippetID, isSnippetID(src) ? fsI(src) : null);
532  
      x = topLevelTranslate(x, libraries, libraries2);
533  
      System.err.println("Translated " + src);
534  
535  
      // save prefetch data
536  
      if (isSnippetID(src))
537  
        savePrefetchData(src);
538  
    }
539  
    return x;
540  
  }
541  
  
542  
  static new ThreadLocal<S> transpilingSnippetID;
543  
544  
  private static void prefetch(String mainSnippetID) throws IOException {
545  
    if (noPrefetch) return;
546  
547  
    long mainID = parseSnippetID(mainSnippetID);
548  
    String s = mainID + " " + loadTextFile(javaxCachesDir("prefetch/" + mainID + ".txt").getPath(), "");
549  
    String[] ids = s.trim().split(" ");
550  
    if (ids.length > 1) {
551  
      String url = tb_mainServer() + "/tb-int/prefetch.php?ids=" + URLEncoder.encode(s, "UTF-8") + standardCredentials();
552  
      String data = loadPage(new URL(url));
553  
      String[] split = data.split(" ");
554  
      if (split.length == ids.length)
555  
        for (int i = 0; i < ids.length; i++)
556  
          prefetched.put(parseSnippetID(ids[i]), split[i]);
557  
    }
558  
  }
559  
560  
  static String userHome() {
561  
    if (android)
562  
      return ((File) call(androidContext, "getFilesDir")).getAbsolutePath();
563  
    else
564  
      return System.getProperty("user.home");
565  
  }
566  
567  
  private static void savePrefetchData(String mainSnippetID) throws IOException {
568  
    List<String> ids = new ArrayList<String>();
569  
    long mainID = parseSnippetID(mainSnippetID);
570  
571  
    for (long id : memSnippetCache.keySet())
572  
      if (id != mainID)
573  
        ids.add(String.valueOf(id));
574  
575  
    saveTextFile(javaxCachesDir("prefetch/" + mainID + ".txt").getPath(), join(" ", ids));
576  
  }
577  
  
578  
  // libraries_out = normal libs
579  
  // libraries2_out = source libs
580  
581  
  static File topLevelTranslate(File srcDir, List<File> libraries_out, LS libraries2_out) throws Exception {
582  
    File x = srcDir;
583  
    x = applyTranslators(x, mainTranslators, libraries_out, libraries2_out); // translators supplied on command line (unusual)
584  
585  
    // actual inner translation of the JavaX source
586  
    x = defaultTranslate(x, libraries_out, libraries2_out);
587  
    return x;
588  
  }
589  
590  
  private static File defaultTranslate(File x, List<File> libraries_out, LS libraries2_out) throws Exception {
591  
    x = luaPrintToJavaPrint(x);
592  
    x = repeatAutoTranslate(x, libraries_out, libraries2_out);
593  
    return x;
594  
  }
595  
596  
  private static File repeatAutoTranslate(File x, List<File> libraries_out, LS libraries2_out) throws Exception {
597  
    List<String[]> postTranslators = new ArrayList;
598  
    
599  
    while (true) {
600  
      String main = loadTextFile(new File(x, "main.java").getPath(), null);
601  
      List<String> lines = toLines(main);
602  
      List<String[]> t = findPostTranslators(lines);
603  
      postTranslators.addAll(t);
604  
      
605  
      if (!t.isEmpty()) {
606  
        main = fromLines(lines);
607  
        x = TempDirMaker_make();
608  
        saveTextFile(new File(x, "main.java").getPath(), main);
609  
      }
610  
611  
      File y = autoTranslate(x, libraries_out, libraries2_out);
612  
      if (y == x)
613  
        break;
614  
      x = y;
615  
    }
616  
    
617  
    x = applyTranslators(x, postTranslators, libraries_out, libraries2_out);
618  
    
619  
    return x;
620  
  }
621  
622  
  private static File autoTranslate(File x, List<File> libraries_out, LS libraries2_out) throws Exception {
623  
    String main = loadTextFile(new File(x, "main.java").getPath(), null);
624  
    L<S[]> translators = new L;
625  
    main = findTranslators2x(main, translators);
626  
    if (verbose) print("autoTranslate: Translators=" + sfu(translators));
627  
    if (translators.isEmpty())
628  
      return x;
629  
630  
    //main = fromLines(lines);
631  
    File newDir = TempDirMaker_make();
632  
    saveTextFile(new File(newDir, "main.java").getPath(), main);
633  
    return applyTranslators(newDir, translators, libraries_out, libraries2_out);
634  
  }
635  
  
636  
  static S findTranslators2x(S src, L<S[]> translatorsOut) {
637  
    L<S> tok = javaTok(src);
638  
    int i;
639  
    while ((i = jfind(tok, "!<int>(")) >= 0) {
640  
      int j = indexOf(tok, ")", i);
641  
      translatorsOut.add(new S[] {tok.get(i+2), join(subList(tok, i+4, j+1))});
642  
      clearTokens(tok, i, j+1);
643  
    }
644  
    while ((i = jfind(tok, "!<int>")) >= 0) {
645  
      translatorsOut.add(new S[] {tok.get(i+2), null});
646  
      clearTokens(tok, i, i+3);
647  
    }
648  
    ret join(tok);
649  
  }
650  
651  
  static List<String[]> findPostTranslators(List<String> lines) {
652  
    List<String[]> translators = new ArrayList<String[]>();
653  
    Pattern pattern = Pattern.compile("^!post\\s*([0-9# \t]+)");
654  
    Pattern pArgs = Pattern.compile("^\\s*\\((.*)\\)");
655  
    for (ListIterator<String> iterator = lines.listIterator(); iterator.hasNext(); ) {
656  
      String line = iterator.next();
657  
      line = line.trim();
658  
      Matcher matcher = pattern.matcher(line);
659  
      if (matcher.find()) {
660  
        String[] t = matcher.group(1).split("[ \t]+");
661  
        String rest = line.substring(matcher.end());
662  
        String arg = null;
663  
        if (t.length == 1) {
664  
          Matcher mArgs = pArgs.matcher(rest);
665  
          if (mArgs.find())
666  
            arg = mArgs.group(1);
667  
        }
668  
        for (String transi : t)
669  
          translators.add(new String[]{transi, arg});
670  
        iterator.remove();
671  
      }
672  
    }
673  
    return translators;
674  
  }
675  
676  
  private static File applyTranslators(File x, List<String[]> translators, List<File> libraries_out, LS libraries2_out) throws Exception {
677  
    for (String[] translator : translators)
678  
      x = applyTranslator(x, translator[0], translator[1], libraries_out, libraries2_out);
679  
    return x;
680  
  }
681  
682  
  // also takes a library
683  
  private static File applyTranslator(File x, String translator, String arg, List<File> libraries_out, LS libraries2_out) throws Exception {
684  
    if (verbose)
685  
      System.out.println("Using translator " + translator + " on sources in " + x.getPath());
686  
687  
    File newDir = runTranslatorOnInput(translator, null, arg, x, !verbose, libraries_out, libraries2_out);
688  
689  
    if (!new File(newDir, "main.java").exists()) {
690  
      throw new Exception("Translator " + translator + " did not generate main.java");
691  
      // TODO: show translator output
692  
    }
693  
    if (verbose)
694  
      System.out.println("Translated with " + translator + " from " + x.getPath() + " to " + newDir.getPath());
695  
    x = newDir;
696  
    return x;
697  
  }
698  
699  
  private static File luaPrintToJavaPrint(File x) throws IOException {
700  
    File newDir = TempDirMaker_make();
701  
    String code = loadTextFile(new File(x, "main.java").getPath(), null);
702  
    code = luaPrintToJavaPrint(code);
703  
    saveTextFile(new File(newDir, "main.java").getPath(), code);
704  
    return newDir;
705  
  }
706  
707  
  public static String luaPrintToJavaPrint(String code) {
708  
    return ("\n" + code).replaceAll(
709  
      "(\n\\s*)print (\".*\")",
710  
      "$1System.out.println($2);").substring(1);
711  
  }
712  
713  
  public static File loadSnippetAsMainJava(String snippetID) throws IOException {
714  
    checkProgramSafety(snippetID);
715  
    File srcDir = TempDirMaker_make();
716  
    saveTextFile(new File(srcDir, "main.java").getPath(), loadSnippet_code(parseSnippetID(snippetID)));
717  
    return srcDir;
718  
  }
719  
720  
  public static File loadSnippetAsMainJavaVerified(String snippetID, String hash) throws IOException {
721  
    checkProgramSafety(snippetID);
722  
    File srcDir = TempDirMaker_make();
723  
    saveTextFile(new File(srcDir, "main.java").getPath(), loadSnippetVerified(snippetID, hash));
724  
    return srcDir;
725  
  }
726  
727  
  @SuppressWarnings( "unchecked" )
728  
  /** returns output dir */
729  
  private static File runTranslatorOnInput(String snippetID, String hash, String arg, File input,
730  
                                           boolean silent,
731  
                                           List<File> libraries_out,
732  
                                           LS libraries2_out) throws Exception {
733  
    if (safeTranslate)
734  
      checkProgramSafetyImpl(snippetID);
735  
    long id = parseSnippetID(snippetID);
736  
    
737  
    File libraryFile = DiskSnippetCache_getLibrary(id);
738  
    
739  
    print("Checking if " + snippetID + " is translator. marked as src lib: " + isMarkedAsSrcLib(snippetID) + ". has lib: " + (libraryFile != null));
740  
    
741  
    if (isMarkedAsSrcLib(snippetID) || isJavaxModuleSnippetType(getSnippetType(snippetID))) {
742  
      setAdd(libraries2_out, snippetID);
743  
      ret input;
744  
    }
745  
    
746  
    if (verbose)
747  
      System.out.println("Library file for " + id + ": " + libraryFile);
748  
    if (libraryFile != null) {
749  
      loadLib(snippetID, libraries_out, libraries2_out, libraryFile);
750  
      return input;
751  
    }
752  
753  
    String[] args = arg != null ? new String[]{arg} : new String[0];
754  
755  
    File srcDir = hash == null ? loadSnippetAsMainJava(snippetID)
756  
      : loadSnippetAsMainJavaVerified(snippetID, hash);
757  
    long mainJavaSize = new File(srcDir, "main.java").length();
758  
759  
    if (verbose)
760  
      System.out.println(snippetID + ": length = " + mainJavaSize);
761  
    if (mainJavaSize == 0) { // no text in snippet? assume it's a library
762  
      loadLib(snippetID, libraries_out, libraries2_out, libraryFile);
763  
      return input;
764  
    }
765  
766  
    List<File> libraries = new ArrayList<File>();
767  
    new LS libraries2;
768  
    Object[] cached = translationCache.get(id);
769  
    if (cached != null) {
770  
      //System.err.println("Taking translator " + snippetID + " from cache!");
771  
      srcDir = (File) cached[0];
772  
      libraries = (List<File>) cached[1];
773  
    } else if (hasTranspiledSet.contains(id) && useServerTranspiled) {
774  
      print("Trying pretranspiled translator: #" + snippetID);
775  
      S transpiledSrc = getServerTranspiled2(snippetID);
776  
      
777  
      int i = transpiledSrc.indexOf('\n');
778  
      S libs = takeFirst(i, transpiledSrc);
779  
      transpiledSrc = transpiledSrc.substring(i+1);
780  
      
781  
      print("libs=" + libs);
782  
      libraries.addAll(javax_librariesToFiles(libs));
783  
      libraries2.addAll(regexpAllMatches("\\d+", libs));
784  
785  
      if (!transpiledSrc.isEmpty()) {
786  
        srcDir = TempDirMaker_make();
787  
        saveTextFile(new File(srcDir, "main.java").getPath(), transpiledSrc);
788  
        translationCache.put(id, cached = new Object[] {srcDir, libraries});
789  
      }
790  
    }
791  
792  
    File ioBaseDir = TempDirMaker_make();
793  
794  
    /*Class<?> mainClass = programCache.get("" + parseSnippetID(snippetID));
795  
    if (mainClass != null)
796  
      return runCached(ioBaseDir, input, args);*/
797  
    // Doesn't work yet because virtualized directories are hardcoded in translator...
798  
799  
    if (cached == null) {
800  
      System.err.println("Translating translator #" + id);
801  
      if (translating.contains(id))
802  
        throw new RuntimeException("Recursive translator reference chain: " + structure(translating));
803  
      translating.add(id);
804  
      try {
805  
        srcDir = defaultTranslate(srcDir, libraries, libraries2);
806  
      } finally {
807  
        translating.remove(id);
808  
      }
809  
      System.err.println("Translated translator #" + id);
810  
      translationCache.put(id, new Object[]{srcDir, libraries});
811  
    }
812  
813  
    boolean runInProcess = false;
814  
815  
    if (virtualizeTranslators) {
816  
      if (verbose) System.out.println("Virtualizing translator");
817  
818  
      // TODO: don't virtualize class _javax (as included in, say, #636)
819  
820  
      //srcDir = applyTranslator(srcDir, "#2000351"); // I/O-virtualize the translator
821  
      // that doesn't work because it recurses infinitely...
822  
823  
      // So we do it right here:
824  
      String s = loadTextFile(new File(srcDir, "main.java").getPath(), null);
825  
      s = s.replaceAll("new\\s+File\\(", "virtual.newFile(");
826  
      s = s.replaceAll("new\\s+FileInputStream\\(", "virtual.newFileInputStream(");
827  
      s = s.replaceAll("new\\s+FileOutputStream\\(", "virtual.newFileOutputStream(");
828  
      s += "\n\n" + loadSnippet("#1004414"); // load class virtual
829  
830  
      // forward snippet cache (virtualized one)
831  
      File dir = virtCache != null ? virtCache : DiskSnippetCache_dir;
832  
      s = s.replace("static File DiskSnippetCache_dir" + ";",
833  
        "static File DiskSnippetCache_dir " + "= new File(" + javaQuote(dir.getAbsolutePath()) + ");"); // extra + is necessary for Dumb TinyBrain :)
834  
      s = s.replace("static boolean preferCached = false;", "static boolean preferCached = true;");
835  
836  
      /*if (verbose) {
837  
        System.out.println("==BEGIN VIRTUALIZED TRANSLATOR==");
838  
        System.out.println(s);
839  
        System.out.println("==END VIRTUALIZED TRANSLATOR==");
840  
      }*/
841  
      
842  
      srcDir = TempDirMaker_make();
843  
      saveTextFile(new File(srcDir, "main.java").getPath(), s);
844  
845  
      // TODO: silence translator also
846  
      runInProcess = true;
847  
    }
848  
849  
    return runJavaX(ioBaseDir, srcDir, input, silent, runInProcess, libraries,
850  
      args, cacheTranslators ? "" + id : null, "" + id);
851  
  }
852  
853  
  static void checkProgramSafety(String snippetID) throws IOException {
854  
    if (!safeOnly) return;
855  
    checkProgramSafetyImpl(snippetID);
856  
  }
857  
858  
  static void checkProgramSafetyImpl(String snippetID) throws IOException {
859  
    URL url = new URL(tb_mainServer() + "/tb-int/is-javax-safe.php?id=" + parseSnippetID(snippetID) + standardCredentials());
860  
    String text = loadPage(url);
861  
    if (!text.startsWith("{\"safe\":\"1\"}"))
862  
      throw new RuntimeException("Program not safe: #" + parseSnippetID(snippetID));
863  
  }
864  
865  
  static void loadLib(String snippetID, List<File> libraries_out, LS libraries2_out, File libraryFile) throws IOException {
866  
    if (verbose)
867  
      System.out.println("Assuming " + snippetID + " is a library.");
868  
869  
    if (libraryFile == null) {
870  
      byte[] data = loadDataSnippetImpl(snippetID);
871  
      DiskSnippetCache_putLibrary(parseSnippetID(snippetID), data);
872  
      libraryFile = DiskSnippetCache_getLibrary(parseSnippetID(snippetID));
873  
    }
874  
875  
    if (!libraries_out.contains(libraryFile)) {
876  
      libraries_out.add(libraryFile);
877  
      libraries2_out.add(str(psI(snippetID)));
878  
    }
879  
  }
880  
881  
  /** returns output dir */
882  
  private static File runJavaX(File ioBaseDir, File originalSrcDir, File originalInput,
883  
                               boolean silent, boolean runInProcess,
884  
                               List<File> libraries, String[] args, String cacheAs,
885  
                               String programID) throws Exception {
886  
    File srcDir = new File(ioBaseDir, "src");
887  
    File inputDir = new File(ioBaseDir, "input");
888  
    File outputDir = new File(ioBaseDir, "output");
889  
    copyInput(originalSrcDir, srcDir);
890  
    copyInput(originalInput, inputDir);
891  
    javax2(srcDir, ioBaseDir, silent, runInProcess, libraries, args, cacheAs, programID, null);
892  
    return outputDir;
893  
  }
894  
895  
  private static void copyInput(File src, File dst) throws IOException {
896  
    copyDirectory(src, dst);
897  
  }
898  
899  
  public static boolean hasFile(File inputDir, String name) {
900  
    return new File(inputDir, name).exists();
901  
  }
902  
903  
  public static void copyDirectory(File src, File dst) throws IOException {
904  
    if (verbose) System.out.println("Copying " + src.getAbsolutePath() + " to " + dst.getAbsolutePath());
905  
    dst.mkdirs();
906  
    File[] files = src.listFiles();
907  
    if (files == null) return;
908  
    for (File file : files) {
909  
      File dst1 = new File(dst, file.getName());
910  
      if (file.isDirectory())
911  
        copyDirectory(file, dst1);
912  
      else {
913  
        if (verbose) System.out.println("Copying " + file.getAbsolutePath() + " to " + dst1.getAbsolutePath());
914  
        copy(file, dst1);
915  
      }
916  
    }
917  
  }
918  
919  
  /** Quickly copy a file without a progress bar or any other fancy GUI... :) */
920  
  public static void copy(File src, File dest) throws IOException {
921  
    FileInputStream inputStream = newFileInputStream(src);
922  
    FileOutputStream outputStream = newFileOutputStream(dest);
923  
    try {
924  
      copy(inputStream, outputStream);
925  
      inputStream.close();
926  
    } finally {
927  
      outputStream.close();
928  
    }
929  
  }
930  
931  
  private static FileInputStream newFileInputStream(File f) throws FileNotFoundException {
932  
    /*if (androidContext != null)
933  
      return (FileInputStream) call(androidContext,
934  
        "openFileInput", f.getPath());
935  
    else*/
936  
    return new // line break for Dumb TinyBrain :)
937  
    FileInputStream(f);
938  
  }
939  
940  
  public static void copy(InputStream in, OutputStream out) throws IOException {
941  
    byte[] buf = new byte[65536];
942  
    while (true) {
943  
      int n = in.read(buf);
944  
      if (n <= 0) return;
945  
      out.write(buf, 0, n);
946  
    }
947  
  }
948  
949  
  public static String loadSnippetVerified(String snippetID, String hash) throws IOException {
950  
    String text = loadSnippet(snippetID);
951  
    String realHash = getHash(text.getBytes("UTF-8"));
952  
    if (!realHash.equals(hash)) {
953  
      String msg;
954  
      if (hash.isEmpty())
955  
        msg = "Here's your hash for " + snippetID + ", please put in your program: " + realHash;
956  
      else
957  
        msg = "Hash mismatch for " + snippetID + ": " + realHash + " (new) vs " + hash + " - has tinybrain.de been hacked??";
958  
      throw new RuntimeException(msg);
959  
    }
960  
    return text;
961  
  }
962  
963  
  public static String getHash(byte[] data) {
964  
    return bytesToHex(getFullFingerprint(data));
965  
  }
966  
967  
  public static byte[] getFullFingerprint(byte[] data) {
968  
    try {
969  
      return MessageDigest.getInstance("MD5").digest(data);
970  
    } catch (NoSuchAlgorithmException e) {
971  
      throw new RuntimeException(e);
972  
    }
973  
  }
974  
975  
  public static long parseSnippetID(String snippetID) {
976  
    return Long.parseLong(shortenSnippetID(snippetID));
977  
  }
978  
979  
  private static String shortenSnippetID(String snippetID) {
980  
    if (snippetID.startsWith("#"))
981  
      snippetID = snippetID.substring(1);
982  
    String httpBlaBla = "http://tinybrain.de/";
983  
    if (snippetID.startsWith(httpBlaBla))
984  
      snippetID = snippetID.substring(httpBlaBla.length());
985  
    return snippetID;
986  
  }
987  
988  
  static String getTranspilationFromBossBot(long snippetID) {
989  
    return boss(format3("get transpilation for *", snippetID));
990  
  }
991  
  
992  
  // This is DIFFERENT from the std library loadSnippet
993  
  // (this is only for code!)
994  
  public static String loadSnippet_code(long snippetID) throws IOException {
995  
    String text = getSnippetFromBossBot(snippetID);
996  
    if (text != null) return text;
997  
    
998  
    if (snippetID >= 1400000 && snippetID < 1500000) ret ""; // binary snippets
999  
      
1000  
    text = memSnippetCache.get(snippetID);
1001  
    if (text != null) {
1002  
      if (verbose)
1003  
        System.out.println("Getting " + snippetID + " from mem cache");
1004  
      return text;
1005  
    }
1006  
    
1007  
    initSnippetCache();
1008  
    text = DiskSnippetCache_get(snippetID);
1009  
    if (preferCached && text != null) {
1010  
      if (verbose)
1011  
        System.out.println("Getting " + snippetID + " from disk cache (preferCached)");
1012  
      return text;
1013  
    }
1014  
1015  
    String md5 = text != null ? md5(text) : "-";
1016  
    if (text != null) {
1017  
      String hash = prefetched.get(snippetID);
1018  
      if (hash != null) {
1019  
        if (md5.equals(hash)) {
1020  
          memSnippetCache.put(snippetID, text);
1021  
          if (verbose)
1022  
            System.out.println("Getting " + snippetID + " from prefetched");
1023  
          return text;
1024  
        } else
1025  
          prefetched.remove(snippetID); // (maybe this is not necessary)
1026  
      }
1027  
    }
1028  
1029  
    try {
1030  
      IResourceLoader rl = vm_getResourceLoader();
1031  
      if (rl != null) {
1032  
        hasTranspiledSet.add(snippetID);
1033  
        // drop libs - is this correct?
1034  
        ret dropFirstLine(rl.getTranspiled(fsI(snippetID)));
1035  
      }
1036  
        
1037  
      String theURL = tb_mainServer() + "/getraw.php?id=" + snippetID + "&getmd5=1&utf8=1&usetranspiled=1" + standardCredentials();
1038  
      if (text != null) {
1039  
        //System.err.println("MD5: " + md5);
1040  
        theURL += "&md5=" + md5;
1041  
      }
1042  
      URL url = new URL(theURL);
1043  
      String page = loadPage(url);
1044  
1045  
      // parse & drop transpilation flag available line
1046  
      int i = page.indexOf('\n');
1047  
      boolean hasTranspiled = page.substring(0, i).trim().equals("1");
1048  
      if (hasTranspiled)
1049  
        hasTranspiledSet.add(snippetID);
1050  
      else
1051  
        hasTranspiledSet.remove(snippetID);
1052  
      page = page.substring(i+1);
1053  
1054  
      if (page.startsWith("==*#*==")) {
1055  
        // same, keep text
1056  
        //System.err.println("Snippet unchanged, keeping.");
1057  
      } else {
1058  
        // drop md5 line
1059  
        i = page.indexOf('\n');
1060  
        String hash = page.substring(0, i).trim();
1061  
        text = page.substring(i+1);
1062  
1063  
        String myHash = md5(text);
1064  
        if (myHash.equals(hash)) {
1065  
          //System.err.println("Hash match: " + hash);
1066  
        } else
1067  
          System.err.println("Hash mismatch");
1068  
      }
1069  
    } catch (Exception e) {
1070  
      printStackTrace(e);
1071  
      throw new IOException("Snippet #" + snippetID + " not found or not public");
1072  
    }
1073  
1074  
    memSnippetCache.put(snippetID, text);
1075  
1076  
    try {
1077  
      initSnippetCache();
1078  
      DiskSnippetCache_put(snippetID, text);
1079  
    } catch (IOException e) {
1080  
      System.err.println("Minor warning: Couldn't save snippet to cache ("  + DiskSnippetCache_getDir() + ")");
1081  
    }
1082  
1083  
    return text;
1084  
  }
1085  
1086  
  /** runs a transpiled set of sources */
1087  
  public static PaA javax2(File srcDir, File ioBaseDir, boolean silent, boolean runInProcess,
1088  
                            List<File> libraries, String[] args, String cacheAs,
1089  
                            String programID, Info info) throws Exception {
1090  
    if (android) {
1091  
      // TODO: no translator virtualization? huh?
1092  
      javax2android(srcDir, args, programID);
1093  
      null;
1094  
    } else {
1095  
      File jarFile = null, classesDir = null;
1096  
      bool jarOK = false;
1097  
      S javacOutput = "";
1098  
      
1099  
      if (useDummyMainClasses()) {
1100  
        //print("Trying to rename " + srcDir + ", " + programID);
1101  
        if (isSnippetID(programID))
1102  
          renameMainJava(srcDir, programID);
1103  
      }
1104  
1105  
      if (isSnippetID(programID)) {
1106  
        S md5 = md5(struct(libraries) + "\n" 
1107  
          + loadTextFile(new File(srcDir, "main.java")) 
1108  
          + loadTextFile(new File(srcDir, dummyMainClassName(programID) + ".java")));
1109  
        S psi = str(parseSnippetID(programID));
1110  
        jarFile = new File(getCacheProgramDir("#1001638"), psi + "-" + md5 + ".jar");
1111  
        jarOK = jarFile.length() >= 50;
1112  
        if (jarOK)
1113  
          classesDir = jarFile;
1114  
      }
1115  
    
1116  
      if (!jarOK) {
1117  
        classesDir = TempDirMaker_make();
1118  
        temp quietLoading ? null : tempLoadingAnim("Compiling Java");
1119  
        javacOutput = compileJava(srcDir, libraries, classesDir);
1120  
      }
1121  
      
1122  
      // save jar
1123  
      
1124  
      if (!jarOK && isSnippetID(programID)) pcall {
1125  
        dir2zip_recurse_verbose = false;
1126  
        dir2jar(classesDir, jarFile);
1127  
      }
1128  
1129  
      // run
1130  
1131  
      if (verbose) System.out.println("Running program (" + srcDir.getAbsolutePath()
1132  
        + ") on io dir " + ioBaseDir.getAbsolutePath() + (runInProcess ? "[in-process]" : "") + "\n");
1133  
      ret runProgram(javacOutput, classesDir, ioBaseDir, silent, runInProcess, libraries, args, cacheAs, programID, info);
1134  
    }
1135  
  }
1136  
1137  
  static Class<?> loadx2android(File srcDir, String programID) throws Exception {
1138  
    // TODO: optimize if it's a loaded snippet anyway
1139  
    URL url = new URL(tb_mainServer() + "/dexcompile.php");
1140  
    URLConnection conn = url.openConnection();
1141  
    String postData = "src=" + URLEncoder.encode(loadTextFile(new File(srcDir, "main.java").getPath(), null), "UTF-8") + standardCredentials();
1142  
    byte[] dexData = doPostBinary(postData, conn);
1143  
    if (!isDex(dexData))
1144  
      throw new RuntimeException("Dex generation error: " + dexData.length + " bytes - " + new String(dexData, "UTF-8"));
1145  
    System.out.println("Dex loaded: " + dexData.length + "b");
1146  
1147  
    File dexDir = TempDirMaker_make();
1148  
    File dexFile = new File(dexDir, System.currentTimeMillis() + ".dex");
1149  
    File dexOutputDir = TempDirMaker_make();
1150  
1151  
    System.out.println("Saving dex to: " + dexDir.getAbsolutePath());
1152  
    try {
1153  
      saveBinaryFile(dexFile.getPath(), dexData);
1154  
    } catch (Throwable e) {
1155  
      System.out.println("Whoa!");
1156  
      throw new RuntimeException(e);
1157  
    }
1158  
1159  
    System.out.println("Getting parent class loader.");
1160  
    ClassLoader parentClassLoader =
1161  
      //ClassLoader.getSystemClassLoader(); // does not find support jar
1162  
      //getClass().getClassLoader(); // Let's try this...
1163  
      x30.class.getClassLoader().getParent(); // XXX !
1164  
1165  
    //System.out.println("Making DexClassLoader.");
1166  
    //DexClassLoader classLoader = new DexClassLoader(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null,
1167  
    //  parentClassLoader);
1168  
    Class dcl = Class.forName("dalvik.system.DexClassLoader");
1169  
    Object classLoader = dcl.getConstructors()[0].newInstance(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null,
1170  
      parentClassLoader);
1171  
1172  
    //System.out.println("Loading main class.");
1173  
    //Class<?> theClass = classLoader.loadClass(mainClassName);
1174  
    Class<?> theClass = (Class<?>) call(classLoader, "loadClass", "main");
1175  
1176  
    //System.out.println("Main class loaded.");
1177  
    try {
1178  
      set(theClass, "androidContext", androidContext);
1179  
    } catch (Throwable e) {}
1180  
1181  
    setVars(theClass, programID);
1182  
    
1183  
    addInstance(programID, theClass);
1184  
    
1185  
    return theClass;
1186  
  }
1187  
  
1188  
  static void addInstance(S programID, Class mainClass) {
1189  
    programID = "" + parseSnippetID(programID);
1190  
    instances.put(programID, new WeakReference<Class>(mainClass));
1191  
  }
1192  
  
1193  
  static Class getInstance(S programID) {
1194  
    programID = "" + parseSnippetID(programID);
1195  
    L<WeakReference<Class>> l = instances.get(programID);
1196  
    for (WeakReference<Class> c : l) {
1197  
      Class theClass = c.get();
1198  
      // TODO: shorten the list
1199  
      if (theClass != null)
1200  
        return theClass;
1201  
    }
1202  
    return null;
1203  
  }
1204  
  
1205  
  static MultiMap<String, WeakReference<Class>> instances = new MultiMap<String, WeakReference<Class>>();
1206  
  
1207  
  !include #1007192 // autoVMExit (core version)
1208  
1209  
  static void javax2android(File srcDir, String[] args, String programID) throws Exception {
1210  
    Class<?> theClass = loadx2android(srcDir, programID);
1211  
1212  
    Method main = null;
1213  
    try {
1214  
      main = findStaticMethod(theClass, "main", new Object[]{androidContext});
1215  
    } catch (RuntimeException e) {
1216  
    }
1217  
1218  
    //System.out.println("main method for " + androidContext + " of " + theClass + ": " + main);
1219  
1220  
    if (main != null) {
1221  
      // old style main program that returns a View
1222  
      // TODO: maybe allow programs without main method, although it doesn't seem to make sense here really (Android main program)
1223  
      System.out.println("Calling main (old-style)");
1224  
      Object view = main.invoke(null, androidContext);
1225  
      System.out.println("Calling setContentView with " + view);
1226  
      call(Class.forName("main"), "setContentViewInUIThread", view);
1227  
      //call(androidContext, "setContentView", view);
1228  
      System.out.println("Done.");
1229  
    } else {
1230  
      System.out.println("New-style main method running.\n\n====\n");
1231  
      runMainMethod(args, theClass);
1232  
    }
1233  
  }
1234  
1235  
  static byte[] DEX_FILE_MAGIC = { 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 };
1236  
1237  
  static boolean isDex(byte[] dexData) {
1238  
    if (dexData.length < DEX_FILE_MAGIC.length) return false;
1239  
    for (int i = 0; i < DEX_FILE_MAGIC.length; i++)
1240  
      if (dexData[i] != DEX_FILE_MAGIC[i])
1241  
        return false;
1242  
    return true;
1243  
  }
1244  
1245  
  static String compileJava(File srcDir, List<File> libraries, File classesDir) throws IOException {
1246  
    javaCompilerOutput = null;
1247  
    ++compilations;
1248  
1249  
    // collect sources
1250  
1251  
    List<File> sources = new ArrayList<File>();
1252  
    if (verbose) System.out.println("Scanning for sources in " + srcDir.getPath());
1253  
    scanForSources(srcDir, sources, true);
1254  
    if (sources.isEmpty())
1255  
      throw new IOException("No sources found");
1256  
1257  
    // compile
1258  
1259  
    File optionsFile = File.createTempFile("javac", "", javaxTempDir());
1260  
    try {
1261  
      if (verbose) System.out.println("Compiling " + sources.size() + " source(s) to " + classesDir.getPath());
1262  
      if (verbose) System.out.println("Libraries: " + libraries);
1263  
      String options = "-d " + bashQuote(classesDir.getPath());
1264  
      libraries.add(pathToJavaxJar());
1265  
      writeOptions(sources, libraries, optionsFile, options);
1266  
      classesDir.mkdirs();
1267  
      return invokeJavaCompiler(optionsFile);
1268  
    } finally {
1269  
      optionsFile.delete();
1270  
    }
1271  
  }
1272  
1273  
  private static PaA runProgram(String javacOutput, File classesDir, File ioBaseDir,
1274  
                                 boolean silent, boolean runInProcess,
1275  
                                 List<File> libraries, String[] args, String cacheAs,
1276  
                                 String programID, Info info) throws Exception {
1277  
    // print javac output if compile failed and it hasn't been printed yet
1278  
    if (info != null) {
1279  
      info.programID = programID;
1280  
      info.programArgs = args;
1281  
    }
1282  
    boolean didNotCompile = !didCompile(classesDir);
1283  
    if (verbose || didNotCompile)
1284  
      System.out.println(javacOutput);
1285  
    if (didNotCompile)
1286  
      null;
1287  
1288  
    if (runInProcess
1289  
      || (ioBaseDir.getAbsolutePath().equals(new File(".").getAbsolutePath()) && !silent)) {
1290  
      ret runProgramQuick(classesDir, libraries, args, cacheAs, programID, info, ioBaseDir);
1291  
    }
1292  
1293  
    boolean echoOK = false;
1294  
    // TODO: add libraries to class path
1295  
    String bashCmd = "(cd " + bashQuote(ioBaseDir.getAbsolutePath()) + " && (java -cp "
1296  
      + bashQuote(classesDir.getAbsolutePath()) + " main" + (echoOK ? "; echo ok" : "") + "))";
1297  
    if (verbose) System.out.println(bashCmd);
1298  
    String output = backtick(bashCmd);
1299  
    lastOutput = output;
1300  
    if (verbose || !silent)
1301  
      System.out.println(output);
1302  
    null;
1303  
  }
1304  
1305  
  // classesDir can also be a .jar
1306  
  static boolean didCompile(File classesDir) {
1307  
    return classesDir.length() >= 50 || hasFile(classesDir, "main.class");
1308  
  }
1309  
1310  
  private static PaA runProgramQuick(File classesDir, List<File> libraries,
1311  
                                      String[] args, String cacheAs,
1312  
                                      String programID, Info info,
1313  
                                      File ioBaseDir) throws Exception {
1314  
    // make class loader
1315  
    JavaXClassLoader classLoader = new JavaXClassLoader(programID, concatLists(ll(classesDir), libraries));
1316  
1317  
    // load JavaX main class
1318  
    Class<?> mainClass = classLoader.loadClass("main");
1319  
    
1320  
    S src = null;
1321  
    if (info != null && info.transpiledSrc != null)
1322  
      src = loadTextFile(findMainJavaInDir(info.transpiledSrc));
1323  
    
1324  
    if (info == null) {
1325  
      //print("no info");
1326  
    } else {
1327  
      //print("info.transpiledSrc = " + info.transpiledSrc);
1328  
      info.mainClass = mainClass;
1329  
      if (hasFieldNamed(mainClass, "myJavaSource_code")) {
1330  
        //print("src = " + l(src));
1331  
        set(mainClass, "myJavaSource_code", src);
1332  
      }
1333  
      if (registerSourceCodes && info.transpiledSrc != null)
1334  
        registerSourceCode(mainClass, src);
1335  
    }
1336  
      
1337  
    if (cacheAs != null)
1338  
      programCache.put(cacheAs, mainClass);
1339  
      
1340  
    // record injection
1341  
    final PaA paa = new PaA(programID, args);
1342  
    paa.injectionID = randomID(8);
1343  
    paa.mainClass = mainClass;
1344  
    if (src != null) paa.srcMD5 = md5(src);
1345  
    addInjection(paa);
1346  
    
1347  
    // set case
1348  
    if (caseID != null)
1349  
      setOpt(mainClass, 'caseID_caseID, caseID);
1350  
1351  
    // change baseDir
1352  
    try {
1353  
      //print("Changing base dir to " + ioBaseDir.getAbsolutePath());
1354  
      Class virtual = mainClass.getClassLoader().loadClass("virtual");
1355  
      set(virtual, "virtual_baseDir", ioBaseDir.getAbsolutePath());
1356  
    } catch (Throwable e) { /* whatever */ }
1357  
1358  
    setVars(mainClass, programID);
1359  
    addInstance(programID, mainClass);
1360  
    paaRun(paa);
1361  
    
1362  
    ret paa;
1363  
  }
1364  
  
1365  
  static void paaRun(PaA paa) {
1366  
    moveThisThreadToChild(paa.mainClass);
1367  
    paa.mainThread = new WeakReference(currentThread());
1368  
    paa.exception = null;
1369  
    try {
1370  
      runMainMethod(paa.arguments, paa.mainClass);
1371  
    } catch (Throwable e) {
1372  
      paa.exception = e;
1373  
      printStackTrace2(e);
1374  
      //throw e;
1375  
    } finally {
1376  
      paa.mainDone = true;
1377  
      synchronized(x30.class) {}
1378  
    }
1379  
  }
1380  
1381  
  static void setVars(Class<?> theClass, String programID) {
1382  
    try {
1383  
      set(theClass, "programID", formatSnippetIDOpt(programID));
1384  
    } catch (Throwable e) {}
1385  
1386  
    try {
1387  
      set(theClass, "__javax", x30.class);
1388  
    } catch (Throwable e) {}
1389  
    
1390  
    callOnLoadMethods(theClass);
1391  
  }
1392  
1393  
1394  
  static void runMainMethod(String[] args, Class<?> mainClass) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
1395  
    callMain(mainClass, args);
1396  
  }
1397  
1398  
  static String invokeJavaCompiler(File optionsFile) throws IOException {
1399  
    long time = now();
1400  
    String output;
1401  
    if (hasEcj() && !javacOnly)
1402  
      output = invokeEcj(optionsFile);
1403  
    else
1404  
      output = invokeJavac(optionsFile);
1405  
    if (verbose) System.out.println(output);
1406  
    if (!quietLoading) done(time, "Java compile");
1407  
    return output;
1408  
  }
1409  
1410  
  private static boolean hasEcj() {
1411  
    try {
1412  
      Class.forName("org.eclipse.jdt.internal.compiler.batch.Main");
1413  
      return true;
1414  
    } catch (ClassNotFoundException e) {
1415  
      return false;
1416  
    }
1417  
  }
1418  
1419  
  // TODO: fix UTF-8 here too
1420  
  private static String invokeJavac(File optionsFile) throws IOException {
1421  
    String output;
1422  
    output = backtick("javac " + bashQuote("@" + optionsFile.getPath()));
1423  
    javaCompilerOutput = output;
1424  
    if (backtick_exitValue != 0) {
1425  
      System.out.println(output);
1426  
      throw new RuntimeException("javac returned errors.");
1427  
    }
1428  
    return output;
1429  
  }
1430  
1431  
  // throws ClassNotFoundException if ecj is not in classpath
1432  
  static String invokeEcj(File optionsFile) {
1433  
    if (vmPort() != 5000 && !dontCompileThroughPort5000) pcall-short {
1434  
      ret invokeEcjInFirstVM(optionsFile);
1435  
    }
1436  
    
1437  
    try {
1438  
      StringWriter writer = new StringWriter();
1439  
      PrintWriter printWriter = new PrintWriter(writer);
1440  
1441  
      // add more eclipse options in the line below
1442  
1443  
      String[] args = {
1444  
        "-source", javaTarget,
1445  
        "-target", javaTarget,
1446  
        "-nowarn",
1447  
        "-encoding", "UTF-8",
1448  
        "@" + optionsFile.getPath()
1449  
      };
1450  
      
1451  
      if (verbose) {
1452  
        print("ECJ options: " + structure(args));
1453  
        print(loadTextFile(optionsFile));
1454  
      }
1455  
1456  
      Class ecjClass = Class.forName("org.eclipse.jdt.internal.compiler.batch.Main");
1457  
      Object main = newInstance(ecjClass, printWriter, printWriter, false);
1458  
      call(main, "compile", new Object[]{args});
1459  
      int errors = (Integer) get(main, "globalErrorsCount");
1460  
1461  
      String output = cleanJavaCompilerOutput(writer.toString());
1462  
      javaCompilerOutput = output;
1463  
      if (errors != 0) {
1464  
        //System.out.println(output);
1465  
        throw new JavaCompileException(output);
1466  
      }
1467  
      return output;
1468  
    } catch (Exception e) {
1469  
      throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
1470  
    }
1471  
  }
1472  
1473  
  static Object newInstance(Class c, Object... args) { try {
1474  
    Constructor m = findConstructor(c, args);
1475  
    m.setAccessible(true);
1476  
    return m.newInstance(args);
1477  
  } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
1478  
1479  
  static Constructor findConstructor(Class c, Object... args) {
1480  
    for (Constructor m : c.getDeclaredConstructors()) {
1481  
      if (!checkArgs(m.getParameterTypes(), args, false))
1482  
        continue;
1483  
      return m;
1484  
    }
1485  
    throw new RuntimeException("Constructor with " + args.length + " matching parameter(s) not found in " + c.getName());
1486  
  }
1487  
1488  
  static boolean checkArgs(Class[] types, Object[] args, boolean debug) {
1489  
    if (types.length != args.length) {
1490  
      if (debug)
1491  
        System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
1492  
      return false;
1493  
    }
1494  
    for (int i = 0; i < types.length; i++)
1495  
      if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
1496  
        if (debug)
1497  
          System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
1498  
        return false;
1499  
      }
1500  
    return true;
1501  
  }
1502  
1503  
  private static void writeOptions(List<File> sources, List<File> libraries,
1504  
                                   File optionsFile, String moreOptions) throws IOException {
1505  
    FileWriter writer = new FileWriter(optionsFile);
1506  
    for (File source : sources)
1507  
      writer.write(bashQuote(source.getPath()) + " ");
1508  
    if (!libraries.isEmpty()) {
1509  
      new LS cp;
1510  
      for (File lib : libraries)
1511  
        cp.add(lib.getAbsolutePath());
1512  
      writer.write("-cp " + bashQuote(join(File.pathSeparator, cp)) + " ");
1513  
    }
1514  
    writer.write(moreOptions);
1515  
    writer.close();
1516  
  }
1517  
1518  
  static void scanForSources(File source, List<File> sources, boolean topLevel) {
1519  
    if (source.isFile() && source.getName().endsWith(".java"))
1520  
      sources.add(source);
1521  
    else if (source.isDirectory() && !isSkippedDirectoryName(source.getName(), topLevel)) {
1522  
      File[] files = source.listFiles();
1523  
      for (File file : files)
1524  
        scanForSources(file, sources, false);
1525  
    }
1526  
  }
1527  
1528  
  private static boolean isSkippedDirectoryName(String name, boolean topLevel) {
1529  
    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.)
1530  
    return name.equalsIgnoreCase("input") || name.equalsIgnoreCase("output");
1531  
  }
1532  
1533  
  public final static String charsetForTextFiles = "UTF8";
1534  
1535  
  static long TempDirMaker_lastValue;
1536  
1537  
  public static File TempDirMaker_make() {
1538  
    File dir = javaxTempDir("" + TempDirMaker_newValue());
1539  
    dir.mkdirs();
1540  
    chmod_aPlusRX(dir);
1541  
    return dir;
1542  
  }
1543  
1544  
  private static long TempDirMaker_newValue() {
1545  
    long value;
1546  
    do
1547  
      value = System.currentTimeMillis();
1548  
    while (value == TempDirMaker_lastValue);
1549  
    TempDirMaker_lastValue = value;
1550  
    return value;
1551  
  }
1552  
1553  
  !include #1000810 // join
1554  
  
1555  
  static String computerID;
1556  
  public static String getComputerID() throws IOException {
1557  
    if (noID) return null;
1558  
    if (computerID == null) {
1559  
      File file = computerIDFile();
1560  
      computerID = loadTextFile(file.getPath());
1561  
      if (computerID == null) {
1562  
        // legacy load
1563  
        computerID = loadTextFile(userDir(".tinybrain/computer-id"));
1564  
        if (computerID == null)
1565  
          computerID = makeRandomID(12);
1566  
        saveTextFile(file.getPath(), computerID);
1567  
      }
1568  
      if (verbose)
1569  
        System.out.println("Local computer ID: " + computerID);
1570  
    }
1571  
    return computerID;
1572  
  }
1573  
1574  
  static void cleanCache() {
1575  
    try {
1576  
      pcall {
1577  
        S s = trim(loadTextFile(getProgramFile("#1001638", "temp-file-retention-time")));
1578  
        if (nempty(s))
1579  
          tempFileRetentionTime = parseInt(s);
1580  
      }
1581  
      cleanJavaXCache(tempFileRetentionTime, verbose);
1582  
    } finally {
1583  
      cleanCacheDone = true;
1584  
    }
1585  
  }
1586  
1587  
  static void showSystemProperties() {
1588  
    System.out.println("System properties:\n");
1589  
    for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
1590  
      System.out.println("  " + entry.getKey() + " = " + entry.getValue());
1591  
    }
1592  
    System.out.println();
1593  
  }
1594  
1595  
  static void showVersion() {
1596  
    //showSystemProperties();
1597  
    boolean eclipseFound = hasEcj();
1598  
    //String platform = System.getProperty("java.vendor") + " " + System.getProperty("java.runtime.name") + " " + System.getProperty("java.version");
1599  
    String platform = System.getProperty("java.vm.name") + " " + System.getProperty("java.version");
1600  
    String os = System.getProperty("os.name"), arch = System.getProperty("os.arch");
1601  
    System.out.println("This is " + version + ", last changed " + dateChanged + ", transpiled " + localDateWithMinutes(myTranspilationDate()) + ".");
1602  
    System.out.println("[Details: " +
1603  
      (eclipseFound ? "Eclipse compiler (good)" : "javac (not so good)")
1604  
      + ", " + platform + ", " + arch + ", " + os + "]");
1605  
  }
1606  
1607  
  static boolean isAndroid() {
1608  
    return System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0;
1609  
  }
1610  
  
1611  
  !include #1000415 // set function
1612  
1613  
  static String smartJoin(String[] args) {
1614  
    String[] a2 = new String[args.length];
1615  
    for (int i = 0; i < args.length; i++) {
1616  
      a2[i] = Pattern.compile("\\w+").matcher(args[i]).matches() ? args[i] : quote(args[i]);
1617  
    }
1618  
    return join(" ", a2);
1619  
  }
1620  
  
1621  
  static void logStart(String[] args) throws IOException {
1622  
    String line = smartJoin(args);
1623  
    appendToLog(new File(userHome(), ".javax/log.txt").getPath(), line);
1624  
  }
1625  
  
1626  
  static void appendToLog(String path, String line) throws IOException {
1627  
    appendToFile(path, "\n" + line + "\n");
1628  
  }
1629  
  
1630  
  static PrintStream oldOut, oldErr;
1631  
  static Thread reader, reader2;
1632  
  static boolean quit; // always false now
1633  
  static PipedInputStream pin=new PipedInputStream(systemOutPipeSize);
1634  
  static PipedInputStream pin2=new PipedInputStream(systemErrPipeSize);
1635  
  static PipedInputStream pin3=new PipedInputStream(systemInPipeSize);
1636  
  static PipedOutputStream pout3;
1637  
1638  
  static class Console /*extends WindowAdapter implements WindowListener,*/ implements ActionListener {
1639  
    JFrame frame;
1640  
    JFastLogView_noWrap logView;
1641  
    JTextField tfInput;
1642  
    JComboBox cbInput; // alternative to tfInput
1643  
    StringBuffer buf = new StringBuffer();
1644  
    JButton buttonclear, buttonkill, buttonrestart, buttonduplicate, buttonassist, buttonpause, buttonhide;
1645  
    String[] args;
1646  
    volatile boolean autoScroll = true;
1647  
    JScrollPane scrollPane;
1648  
    S title;
1649  
    JLabel memoryView;
1650  
1651  
    final DelayedUpdate du = new DelayedUpdate(r {
1652  
      bool show = !consoleUpdateOff && isVisibleFrame(frame);
1653  
      S text = "";
1654  
      synchronized(buf) {
1655  
        int max = maxConsoleChars;
1656  
        if (buf.length() > max) {
1657  
          try {
1658  
            int newLength = max/2;
1659  
            int ofs = buf.length()-newLength;
1660  
            S newString = buf.substring(ofs);
1661  
            buf.setLength(0);
1662  
            buf.append("[...] ").append(newString);
1663  
          } catch (Exception e) {
1664  
            buf.setLength(0);
1665  
          }
1666  
          buf.trimToSize();
1667  
        }
1668  
1669  
        if (show) text = str(buf);
1670  
      }
1671  
      
1672  
      if (!show) ret;
1673  
      
1674  
      // TODO: optimize more?
1675  
      if (logView != null && logView.setText(text) && autoScroll)
1676  
        scrollAllTheWayDown(logView);
1677  
    });
1678  
1679  
    public Console(final String[] args) ctex {
1680  
      du.delay = consoleUpdateDelay;
1681  
      this.args = args;
1682  
      // create all components and add them
1683  
      frame = new JFrame(args.length == 0 ? "JavaX Starter Output" : "JavaX Output - " + join(" ", args));
1684  
      if (!consoleGrabFocus)
1685  
        frame.setAutoRequestFocus(false);
1686  
1687  
    /*Dimension screenSize=Toolkit.getDefaultToolkit().getScreenSize();
1688  
    Dimension frameSize=new Dimension((int)(screenSize.width/2),(int)(screenSize.height/2));
1689  
    int x=(int)(frameSize.width/2);
1690  
    int y=(int)(frameSize.height/2);
1691  
    frame.setBounds(x,y,frameSize.width,frameSize.height);*/
1692  
1693  
      // put in bottom-right corner
1694  
      Rectangle r = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
1695  
      frame.setBounds(r.x+r.width-consoleWidth-consoleXGap, r.y+r.height-consoleHeight-consoleYGap, consoleWidth, consoleHeight);
1696  
      moveWindowIntoScreen(frame);
1697  
1698  
      if (verbose) print("Making log view");
1699  
      logView = jFastLogView_noWrap();
1700  
      logView.verbose = verbose;
1701  
      if (verbose) print("Made log view");
1702  
      buttonclear = consoleButton("clear");
1703  
      buttonkill = consoleButton("kill");
1704  
      buttonrestart = consoleButton("restart");
1705  
      buttonrestart.setToolTipText("Restart program");
1706  
      buttonduplicate = consoleButton("duplicate");
1707  
      buttonassist = consoleButton("assist");
1708  
      buttonpause = consoleButton("pause");
1709  
      buttonhide = consoleButton("hide");
1710  
      JLabel mv = memoryView = jMemoryView();
1711  
      JPanel buttons = westAndCenter(withRightMargin(mv), hgrid(
1712  
        buttonclear,
1713  
        buttonkill,
1714  
        buttonrestart,
1715  
        buttonduplicate,
1716  
        buttonassist,
1717  
        buttonpause,
1718  
        buttonhide));
1719  
        
1720  
      componentPopupMenuItem(mv, "Re-run main", r reRunMain);
1721  
      componentPopupMenuItem(mv, "Print stack traces", r listUserThreadsWithStackTraces);
1722  
      componentPopupMenuItem(mv, "10 second profile", r swing_tenSecondProfile);
1723  
      componentPopupMenuItem(mv, "Hide VM", r hideVM);
1724  
      componentPopupMenuItem(mv, "Set variable...", r setVarDialog);
1725  
      
1726  
      componentPopupMenu(mv, voidfunc(JPopupMenu menu) {
1727  
        if (isOfflineMode())
1728  
          addMenuItem(menu, "Switch to online mode", r goOnlineMode);
1729  
        else
1730  
          addMenuItem(menu, "Switch to offline mode", r goOfflineMode);
1731  
      });
1732  
      
1733  
      //componentPopupMenuItem(mv, "Bigger fonts", r swingBiggerFonts);
1734  
      //componentPopupMenuItem(mv, "Smaller fonts", r swingSmallerFonts);
1735  
1736  
      pout3 = new PipedOutputStream(pin3);
1737  
      
1738  
      tfInput = new JTextField();
1739  
      tfInput.addActionListener(actionListener {
1740  
        S line = tfInput.getText();
1741  
        try {
1742  
          pout3.write((line + "\n").getBytes("UTF-8"));
1743  
          pout3.flush();
1744  
        } catch (Exception e) {}
1745  
        //tfInput.setText("");
1746  
        tfInput.selectAll();
1747  
      });
1748  
      addHistoryToTextField(tfInput);
1749  
      
1750  
      JPanel panel = new JPanel(new BorderLayout());
1751  
      if (verbose) print("Making scroll pane");
1752  
      panel.add(scrollPane = jscroll_copyBackground(logView), BorderLayout.CENTER);
1753  
      if (verbose) print("Made scroll pane");
1754  
      panel.add(tfInput, BorderLayout.SOUTH);
1755  
      
1756  
      frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
1757  
      frame.addWindowListener(new WindowAdapter() {
1758  
        public void windowActivated(WindowEvent e) {
1759  
          if (focusConsoleInputOnActivate)
1760  
            tfInput.requestFocus();
1761  
        }
1762  
        
1763  
        public synchronized void windowClosing(WindowEvent evt) {
1764  
          if (JOptionPane.showConfirmDialog(frame,
1765  
            "Close " + (empty(title) ? "JavaX Application" : title) + "?", "JavaX", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
1766  
            cleanKillVM_noSleep();
1767  
        }
1768  
      });
1769  
      
1770  
      frame.getContentPane().setLayout(new BorderLayout());
1771  
      frame.getContentPane().add(panel, BorderLayout.CENTER);
1772  
      frame.getContentPane().add(buttons, BorderLayout.SOUTH);
1773  
      if (verbose) print("Showing console frame");
1774  
      if (consoleOn) frame.setVisible(true);
1775  
      if (verbose) print("Showed console frame");
1776  
      setFrameIconLater(frame, javaxDefaultIcon());
1777  
      
1778  
      //frame.addWindowListener(this); // disabled for now
1779  
      buttonclear.addActionListener(this);
1780  
      buttonkill.addActionListener(this);
1781  
      buttonrestart.addActionListener(this);
1782  
      buttonduplicate.addActionListener(this);
1783  
      buttonassist.addActionListener(this);
1784  
      buttonpause.addActionListener(this);
1785  
      buttonhide.addActionListener(this);
1786  
1787  
      quit=false; // signals the Threads that they should exit
1788  
      
1789  
      if (args.length != 0) {
1790  
        //print("Starting title updater");
1791  
        new Thread("Console Title Updater :)") {
1792  
          public void run() {
1793  
            if (args.length != 0) {
1794  
              int i = 0;
1795  
              while (i < args.length && !isSnippetID(args[i])) {
1796  
                if (eq(args[i], "-case")) ++i;
1797  
                ++i;
1798  
              }
1799  
              //print("Getting title for " + args[i]);
1800  
              title = getSnippetTitle(get(args, i));
1801  
              //print("Title: " + title);
1802  
              if (title != null && title.length() != 0)
1803  
                frame.setTitle(title + " [Output]");
1804  
            }
1805  
          }
1806  
        }.start();
1807  
      }
1808  
      
1809  
      System.setIn(pin3);
1810  
      
1811  
    }
1812  
    
1813  
    void scrollToBottomLater {
1814  
      awt { scrollToBottom(); }
1815  
    }
1816  
    
1817  
    void scrollToBottom {
1818  
      JScrollBar vertical = scrollPane.getVerticalScrollBar();
1819  
      vertical.setValue(vertical.getMaximum());
1820  
    }
1821  
1822  
    /*public synchronized void windowClosed(WindowEvent evt)
1823  
    {
1824  
      console = null;
1825  
      //quit=true;
1826  
      //this.notifyAll(); // stop all threads
1827  
      //try { reader.join(1000);pin.close();   } catch (Exception e){}
1828  
      //try { reader2.join(1000);pin2.close(); } catch (Exception e){}
1829  
      //System.exit(0);
1830  
    }*/
1831  
1832  
    /*public synchronized void windowClosing(WindowEvent evt)
1833  
    {
1834  
      frame.setVisible(false); // default behaviour of JFrame
1835  
      frame.dispose();
1836  
    }*/
1837  
1838  
    public synchronized void actionPerformed(ActionEvent evt) {
1839  
      if (evt.getSource() == buttonkill) {
1840  
        cleanKillVM_noSleep();
1841  
      } else if (evt.getSource() == buttonrestart) {
1842  
        cleanRestart(args);
1843  
      } else if (evt.getSource() == buttonduplicate) {
1844  
        print("Console: Duplicate button pressed.");
1845  
        nohupJavax(smartJoin(args));
1846  
      } else if (evt.getSource() == buttonassist) {
1847  
        assist();
1848  
      } else if (evt.getSource() == buttonpause) {
1849  
        O mainClass = getMainMainClass();
1850  
        if (mainClass != null) {
1851  
          if (eq(buttonpause.getText(), "pause")) {
1852  
            buttonpause.setText("resume");
1853  
            pauseAll(true);
1854  
          } else {
1855  
            buttonpause.setText("pause");
1856  
            pauseAll(false);
1857  
          }
1858  
        }
1859  
      } else if (evt.getSource() == buttonhide) {
1860  
        hideConsole();
1861  
      } else { // clear log
1862  
        if (logView != null) logView.setText("");
1863  
        buf = new StringBuffer();
1864  
      }
1865  
    }
1866  
    
1867  
    void showConsole() {
1868  
      swing {
1869  
        if (!frame.isVisible()) {
1870  
          makeFrameVisible(frame);
1871  
          du.trigger();
1872  
        }
1873  
      }
1874  
    }
1875  
1876  
    void hideConsole() {
1877  
      autoVMExit();
1878  
      frame.setVisible(false);
1879  
    }
1880  
    
1881  
    public void appendText(String s, boolean outNotErr) {
1882  
      //if (verbose) oldOut.println("Console appendText " + outNotErr + " " + quote(s));
1883  
      synchronized(buf) {
1884  
        buf.append(s);
1885  
      }
1886  
      du.trigger();
1887  
    }
1888  
  } // Console
1889  
1890  
  static void tryToOpenConsole(String[] args) {
1891  
    try {
1892  
      if (isHeadless()) ret;
1893  
      swing {
1894  
        if (console == null)
1895  
          console = new Console(args);
1896  
      }
1897  
    } catch (HeadlessException e) {
1898  
      // ok, we're headless.
1899  
    } catch (Throwable e) {
1900  
      // some other error in console - continue without it
1901  
      printStackTrace(e);
1902  
    }
1903  
  }
1904  
1905  
  //// END CONSOLE STUFF
1906  
1907  
  static long now_virtualTime;
1908  
  static long now() {
1909  
    return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis();
1910  
  }
1911  
1912  
  static <A> A print(A a) {
1913  
    System.out.println(a);
1914  
    ret a;
1915  
  }
1916  
  
1917  
  static void print_noNewLine(String s) {
1918  
    System.out.print(s);
1919  
  }
1920  
  
1921  
  static void print() { print(""); }
1922  
  
1923  
  static <A> A print(S s, A o) {
1924  
    print((endsWithLetterOrDigit(s) ? s + ": " : s) + o);
1925  
    ret o;
1926  
  }
1927  
1928  
  static runnable class StdOutPiper {
1929  
    try {
1930  
      new UTF8Processor p;
1931  
      while (Thread.currentThread()==reader) {
1932  
        sleep(pipeDelay);
1933  
        if (pin.available()!=0) {
1934  
          String input=readLineThroughUTF8Processor(p, pin);
1935  
          //if (verbose) oldOut.println("reader: " + quote(input));
1936  
          appendText(input, true);
1937  
        }
1938  
        if (quit) return;
1939  
      }
1940  
1941  
      while (Thread.currentThread()==reader2) {
1942  
        sleep(pipeDelay);
1943  
        if (pin2.available()!=0) {
1944  
          String input=readLineThroughUTF8Processor(p, pin2);
1945  
          //if (verbose) oldOut.println("reader2: " + quote(input));
1946  
          appendText(input, false);
1947  
        }
1948  
        if (quit) return;
1949  
      }
1950  
    } catch (Exception e) {
1951  
      appendText("\nConsole reports an Internal error.", false);
1952  
      appendText("The error is: "+e, false);
1953  
    }
1954  
  }
1955  
1956  
  static void redirectSystemOutAndErr() {
1957  
    if (reader != null) return; // did this already
1958  
1959  
    StdOutPiper _this = new StdOutPiper;
1960  
    
1961  
    if (verbose) print("Redirecting System.out");
1962  
    try
1963  
    {
1964  
      PipedOutputStream pout=new PipedOutputStream(pin);
1965  
      oldOut = System.out;
1966  
      TeeOutputStream tee = new TeeOutputStream(oldOut, pout);
1967  
      System.setOut(new PrintStream(tee,true));
1968  
    }
1969  
    catch (Exception io)
1970  
    {
1971  
      System.err.println("Couldn't redirect STDOUT - " + io.getMessage());
1972  
    }
1973  
1974  
    if (verbose) System.out.println("Redirecting System.err");
1975  
    try
1976  
    {
1977  
      PipedOutputStream pout2=new PipedOutputStream(pin2);
1978  
      oldErr = System.err;
1979  
      TeeOutputStream tee = new TeeOutputStream(oldErr, pout2);
1980  
      System.setErr(new PrintStream(tee,true));
1981  
    }
1982  
    catch (Exception io)
1983  
    {
1984  
      System.err.println("Couldn't redirect STDERR - " + io.getMessage());
1985  
    }
1986  
1987  
    if (verbose) System.out.println("Redirects done. Starting readers");
1988  
    
1989  
    // Starting two seperate threads to read from the PipedInputStreams
1990  
    //
1991  
    reader=new Thread(_this, "StdOut Piper");
1992  
    reader.setDaemon(true);
1993  
    reader.start();
1994  
    //
1995  
    reader2 = new Thread(_this, "StdErr Piper");
1996  
    reader2.setDaemon(true);
1997  
    reader2.start();
1998  
  }
1999  
2000  
  static Appendable customSystemOut;
2001  
  static StringBuffer outBuf; // optional all-logging
2002  
  
2003  
  static new ArrayDeque<S> systemInBuf;
2004  
2005  
  static void appendText(String s, boolean outNotErr) {
2006  
    if (empty(s)) ret;
2007  
    // We do this with a TeeOutputStream now (safer).
2008  
    // (outNotErr ? oldOut : oldErr).print(s);
2009  
    
2010  
    if (console != null)
2011  
      console.appendText(s, outNotErr);
2012  
    if (outBuf != null)
2013  
      outBuf.append(s);
2014  
    if (customSystemOut != null)
2015  
      try {
2016  
        customSystemOut.append(s);
2017  
      } catch (IOException e) {
2018  
        printStackTrace(e);
2019  
      }
2020  
  }
2021  
2022  
  /*static String readLine(PipedInputStream in) throws IOException
2023  
  {
2024  
    new StringBuilder input;
2025  
    do
2026  
    {
2027  
      int available=in.available();
2028  
      if (available==0) break;
2029  
      byte b[]=new byte[available];
2030  
      in.read(b);
2031  
      S s = new String(b,0,b.length);
2032  
      input.append(s);
2033  
      if (s.contains("\n")) break;
2034  
    // }while( !input.endsWith("\n") &&  !input.endsWith("\r\n") && !quit);
2035  
    } while (!quit);
2036  
    return str(input);
2037  
  }*/
2038  
  
2039  
  static void autoReportToChat() {
2040  
    if (customSystemOut == null) {
2041  
      print("Auto-reporting to chat.");
2042  
      customSystemOut = new Appendable() {
2043  
        LineBuf buf = new LineBuf();
2044  
        
2045  
        // only using this one
2046  
        public Appendable append(CharSequence cs) {
2047  
          buf.append(cs.toString());
2048  
          while (true) {
2049  
            String s = buf.nextLine();
2050  
            if (s == null) break;
2051  
            reportToChat(s, true);
2052  
          }
2053  
          return this;
2054  
        }
2055  
    
2056  
        public Appendable append(char c) { return this; }
2057  
        public Appendable append(CharSequence s, int start, int end) { return this; }
2058  
      };
2059  
     }
2060  
  }
2061  
2062  
static void reportToChat(final String s, boolean silent) {
2063  
    if (s == null || s.length() == 0) return;
2064  
    if (!silent)
2065  
      print("reportToChat: " + quote(s));
2066  
    reportToChat_getChatThread().add(new Runnable() {
2067  
    public void run() { try {
2068  
        startChatServerIfNotUp();
2069  
        waitForChatServer();
2070  
        chatSend(s);
2071  
    } catch (Exception __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}});
2072  
   }
2073  
  
2074  
  static Q reportToChat_q;
2075  
  
2076  
  static Q reportToChat_getChatThread() {
2077  
    if (reportToChat_q == null)
2078  
      reportToChat_q = new Q("reportToChat");
2079  
    return reportToChat_q;
2080  
  }
2081  
  
2082  
  static void startChatServerIfNotUp() {
2083  
    if (portIsBound(9751)) {
2084  
      //print("Chat seems to be up.");
2085  
    } else {
2086  
      nohupJavax("1000867");
2087  
      print("Chat server should be coming up any minute now.");
2088  
    }
2089  
  }
2090  
  
2091  
  static void waitForChatServer() {
2092  
    if (!portIsBound(9751)) {
2093  
      //System.out.print("Waiting for chat server... ");
2094  
      do {
2095  
        sleep(1000);
2096  
      } while (!portIsBound(9751));
2097  
      //print("OK.");
2098  
    }
2099  
  }
2100  
2101  
  static boolean portIsBound(int port) {
2102  
    try {
2103  
      ServerSocket s = new ServerSocket(port);
2104  
      s.close();
2105  
      return false;
2106  
    } catch (IOException e) {
2107  
      return true;
2108  
    }
2109  
  }
2110  
  
2111  
  static class LineBuf {
2112  
    StringBuffer buf = new StringBuffer();
2113  
    
2114  
    void append(String s) {
2115  
      buf.append(s);
2116  
    }
2117  
    
2118  
    String nextLine() {
2119  
      int i = buf.indexOf("\n");
2120  
      if (i >= 0) {
2121  
        String s = buf.substring(0, i > 0 && buf.charAt(i-1) == '\r' ? i-1 : i);
2122  
        buf.delete(0, i+1);
2123  
        return s;
2124  
      }
2125  
      return null;
2126  
    }
2127  
  } // LineBuf
2128  
2129  
  !include #1000937 // chatSend
2130  
2131  
  static class TeeOutputStream extends OutputStream {
2132  
    
2133  
    protected OutputStream out, branch;
2134  
2135  
    public TeeOutputStream( OutputStream out, OutputStream branch ) {
2136  
      this.out = out;
2137  
      this.branch = branch;
2138  
    }
2139  
2140  
    @Override
2141  
    public synchronized void write(byte[] b) throws IOException {
2142  
      write(b, 0, b.length);
2143  
    }
2144  
2145  
    @Override
2146  
    public synchronized void write(byte[] b, int off, int len) throws IOException {
2147  
      //if (verbose) oldOut.println("Tee write " + new String(b, "UTF-8"));
2148  
      out.write(b, off, len);
2149  
      this.branch.write(b, off, len);
2150  
    }
2151  
2152  
    @Override
2153  
    public synchronized void write(int b) throws IOException {
2154  
      write(new byte[] {(byte) b});
2155  
    }
2156  
2157  
    /**
2158  
     * Flushes both streams.
2159  
     * @throws IOException if an I/O error occurs
2160  
     */
2161  
    @Override
2162  
    public void flush() throws IOException {
2163  
      out.flush();
2164  
      this.branch.flush();
2165  
    }
2166  
2167  
    /**
2168  
     * Closes both streams.
2169  
     * @throws IOException if an I/O error occurs
2170  
     */
2171  
    @Override
2172  
    public void close() throws IOException {
2173  
      out.close();
2174  
      this.branch.close();
2175  
    }
2176  
  }
2177  
  
2178  
  static boolean isChatServer(String[] args) {
2179  
    for (int i = 0; i < args.length; i++)
2180  
      if (isSnippetID(args[i]))
2181  
        return parseSnippetID(args[i]) == 1000867;
2182  
    return false;
2183  
  }
2184  
  
2185  
  static void listUserThreadsWithStackTraces() {
2186  
    print("");
2187  
    Map<Thread, StackTraceElement[]> threadMap = Thread.getAllStackTraces();
2188  
    int n = 0;
2189  
    for (Thread t : threadMap.keySet()) {
2190  
      ThreadGroup g = t.getThreadGroup();
2191  
      if (g != null && g.getName().equals("system")) continue;
2192  
      ++n;
2193  
      print(t);
2194  
      for (StackTraceElement e : threadMap.get(t)) {
2195  
        print("  " + e);
2196  
      }
2197  
      print("");
2198  
    }
2199  
    print(n + " user threads.");
2200  
  }
2201  
  
2202  
  static void makeVMAndroid() {
2203  
    Android3 a = new Android3("This is a JavaX VM.");
2204  
    a.responder = new Responder() {
2205  
      S answer(S s, L<S> history) {
2206  
        return x30.answer(s, history);
2207  
      }
2208  
    };
2209  
    a.daemon = true;
2210  
    a.console = false;
2211  
    a.incomingSilent = true; // to avoid too much printing
2212  
    a.useMultiPort = false;
2213  
    a.quiet = true;
2214  
    makeAndroid3(a);
2215  
    
2216  
    new MultiPort; // auto-add multi-port
2217  
    
2218  
    sendOptInNewThreadQuietly("VM Lister.", "started vm * *", vmPort(), vmID());
2219  
  }
2220  
  
2221  
  static class Info {
2222  
    S programID;
2223  
    S[] programArgs;
2224  
    File transpiledSrc;
2225  
    Class mainClass;
2226  
  }
2227  
  
2228  
  static new Info info; // hmm...
2229  
  
2230  
  // injectable info
2231  
  
2232  
  static new L<PaA> injectable_programsInjected;
2233  
  static boolean injectable_on = true;
2234  
  static class PaA { // "Program and Arguments"
2235  
    S injectionID;
2236  
    S progID;
2237  
    S[] arguments;
2238  
    transient Class mainClass; // TODO: release eventually...
2239  
    transient WeakReference<Thread> mainThread;
2240  
    volatile boolean mainDone;
2241  
    transient volatile Throwable exception;
2242  
    S srcMD5;
2243  
    
2244  
    *(S *progID, S[] *arguments) {}
2245  
    *() {}
2246  
  }
2247  
  
2248  
  static S vmID = makeRandomID(10);
2249  
  
2250  
  static synchronized void addInjection(PaA paa) {
2251  
    injectable_programsInjected.add(paa);
2252  
  }
2253  
  
2254  
  static synchronized void removeInjection(PaA paa) {
2255  
    cleanUp(paa.mainClass);
2256  
    injectable_programsInjected.remove(paa);
2257  
  }
2258  
  
2259  
  static synchronized L<PaA> getInjections() {
2260  
    ret cloneList(injectable_programsInjected);
2261  
  }
2262  
  
2263  
  static S answer(S s, L<S> history) ctex {
2264  
    new Matches m;
2265  
    
2266  
    if (match3("kill!", s)) {
2267  
      cleanKill();
2268  
      return "ok";
2269  
    }
2270  
    if (match3("restart", s)) {
2271  
      cleanRestart(fullArgs);
2272  
      ret "ok";
2273  
    }
2274  
    if (match3("What is your process ID?", s) || match3("what is your pid?", s))
2275  
      return getPID();
2276  
    if (match3("get vm id", s))
2277  
      return vmID;
2278  
    if (match3("what is the javax program id?", s))
2279  
      return javaxProgramID;
2280  
    if (match3("what is the main program id?", s))
2281  
      return info.programID;
2282  
    if (match3("what are your program arguments?", s))
2283  
      return structure(info.programArgs);
2284  
    if (match3("get output", s))
2285  
      return outBuf != null ? quote(outBuf.toString()) : "Not logged";
2286  
    if (match3("get output length", s))
2287  
      return outBuf != null ? "" + outBuf.length() : "Not logged";
2288  
    if (match3("get output substring from *", s, m))
2289  
      return quote(outBuf.substring(parseInt(m.get(0))));
2290  
    if (match3("get output substring from * to *", s, m))
2291  
      return quote(outBuf.substring(parseInt(m.get(0)), parseInt(m.get(1))));
2292  
    if (match3("clear output", s)) {
2293  
      synchronized(x30.class) {
2294  
        int len = outBuf.length();
2295  
        outBuf.setLength(0);
2296  
        outBuf.trimToSize();
2297  
        return "OK, deleted " + len + " chars.";
2298  
      }
2299  
    }
2300  
    if (match3("send line * to system.in", s, m)) {
2301  
      S line = m.unq(0);
2302  
      synchronized(x30.class) {
2303  
        systemInBuf.add(line);
2304  
      }
2305  
      ret "ok";
2306  
    }
2307  
    if (match3("get fields of main class", s))
2308  
      return structure(listFields(info.mainClass));
2309  
    if (match3("get field * of main class", s, m))
2310  
      return structure(get(info.mainClass, m.m[0]));
2311  
    if (match3("invoke function * of main class", s, m))
2312  
      return structure(call(info.mainClass, m.m[0]));
2313  
    if (match3("set field * of main class to *", s, m)) {
2314  
      set(info.mainClass, m.m[0], unstructure(m.m[1]));
2315  
      return "ok";
2316  
    }
2317  
    if (match3("how much memory are you consuming", s))
2318  
      return "Java heap size: " + (Runtime.getRuntime().totalMemory()+1024*1024-1)/1024/1024 + " MB";
2319  
    if (match3("how much memory is used after GC?", s)) {
2320  
      gc();
2321  
      return "Java heap used: " + (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()+1024*1024-1)/1024/1024 + " MB";
2322  
    }
2323  
    if (match3("how much memory is used?", s))
2324  
      return "Java heap used: " + (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()+1024*1024-1)/1024/1024 + " MB";
2325  
      
2326  
    if (match3("please run program *", s, m) || match3("please run program * with arguments *", s, m)) {
2327  
      final S progID = $1;
2328  
      final S[] arguments = m.m.length > 1 ? toStringArray(unstructure($2)) : new S[0];
2329  
      final PaA paa = preInjectProgram(progID, arguments);
2330  
      // program runs in same thread
2331  
      paaRun(paa);
2332  
      Throwable error = paa.exception;
2333  
      O result = getOpt(paa.mainClass, "result");
2334  
      removeInjection(paa);
2335  
      ret error != null ? "Runtime Error: " + getStackTrace(error) : "OK " + struct(result);
2336  
    }
2337  
    
2338  
    if (match3("please inject program *", s, m) || match3("please inject program * with arguments *", s, m)) {
2339  
      final S progID = $1;
2340  
      final S[] arguments = m.m.length > 1 ? toStringArray(unstructure($2)) : new S[0];
2341  
      final PaA paa = preInjectProgram(progID, arguments);
2342  
      // program may run in its own thread.
2343  
      thread progID { paaRun(paa); }
2344  
      ret format3("OK. Injection ID: *", paa.injectionID);
2345  
    }
2346  
    
2347  
    if (match3("get injection exception *", s, m)) {
2348  
      S injectionID = unquote(m.m[0]);
2349  
      PaA paa = findInjection(injectionID);
2350  
      if (paa == null)
2351  
        ret "Sorry. Injection not found";
2352  
      ret "OK: " + paa.exception == null ? "no exception" : getStackTrace(paa.exception);
2353  
    }
2354  
     
2355  
    if (match3("get injection * variable *", s, m)) {
2356  
      S injectionID = unquote(m.m[0]);
2357  
      S var = unquote(m.m[1]);
2358  
      PaA paa = findInjection(injectionID);
2359  
      if (paa == null)
2360  
        ret "Sorry. Injection not found";
2361  
      ret "OK: " + structure(getOpt(paa.mainClass, var));
2362  
    }
2363  
     
2364  
    if (match3("get injection result *", s, m)) {
2365  
      S injectionID = unquote(m.m[0]);
2366  
      PaA paa = findInjection(injectionID);
2367  
      if (paa == null)
2368  
        ret "Sorry. Injection not found";
2369  
      ret "OK: " + structure(getOpt(paa.mainClass, "result"));
2370  
    }
2371  
     
2372  
    if (match3("is injection's * main done", s, m)) {
2373  
      S injectionID = unquote(m.m[0]);
2374  
      PaA paa = findInjection(injectionID);
2375  
      if (paa == null)
2376  
        ret "Sorry. Injection not found";
2377  
      ret paa.mainDone ? "Yes." : "No.";
2378  
    }
2379  
    
2380  
    if (match3("get injections", s, m)) {
2381  
      ret structure(getInjections());
2382  
    }
2383  
    
2384  
    if (match3("remove injection *", s, m)) {
2385  
      S injectionID = unquote(m.m[0]);
2386  
      PaA paa = findInjection(injectionID);
2387  
      if (paa == null)
2388  
        ret "Sorry. Injection not found";
2389  
      removeInjection(paa);
2390  
      ret "OK, removed.";
2391  
    }
2392  
    
2393  
    if (match3("which programs are you running (ids only)", s, m)) {
2394  
      synchronized(x30.class) {
2395  
        new L<S> l;
2396  
        for (S progID : instances.keySet())
2397  
          if (getInstance(progID) != null)
2398  
            l.add(progID);
2399  
        return format3("these: *", structure(l));
2400  
      }
2401  
    }
2402  
    
2403  
    if "show console"
2404  
      ret _showConsole();
2405  
2406  
    if "hide console" {
2407  
      if (console != null) {
2408  
        console.hideConsole();
2409  
        ret "ok";
2410  
      }
2411  
      ret "no console";
2412  
    }
2413  
     
2414  
    L multiPorts = getMultiPorts();
2415  
    if (!multiPorts.isEmpty()) {
2416  
      O multiPort = multiPorts.get(0);
2417  
      S answer = makeResponder(multiPort).answer(s, history);
2418  
      if (answer != null) ret answer;
2419  
    }
2420  
    
2421  
    if "list bots"
2422  
      ret structure(litmap());
2423  
      
2424  
    if "get last files written"
2425  
      ret structure(cloneList(lastFilesWritten));
2426  
      
2427  
    if "dump heap to *" {
2428  
      dumpHeap(new File(assertAbsolutePath($1)));
2429  
      ret "OK";
2430  
    }
2431  
    
2432  
    if "prepare for javax upgrade" {
2433  
      loadMyClasses();
2434  
      ret "OK";
2435  
    }
2436  
    
2437  
    if "invoke ecj on options file *" {
2438  
      long time = sysNow();
2439  
      print("Invoking ECJ: " + $1);
2440  
      S answer = "ok " + invokeEcj(new File($1));
2441  
      done2(time, "ecj");
2442  
      ret answer;
2443  
    }
2444  
    
2445  
    // These don't show or hide any windows; it's just a flag
2446  
    // that says whether the VM is allowed to run without any
2447  
    // windows.
2448  
    
2449  
    // This does hide the console actually
2450  
    if "hidden vm yes" {
2451  
      hideVM();
2452  
      ret "OK";
2453  
    }
2454  
    
2455  
    if "hidden vm no" {
2456  
      hiddenVM = false;
2457  
      sendOpt("VM Lister.", "unhiding vm " + vmPort());
2458  
      ret "OK";
2459  
    }
2460  
    
2461  
    if "is hidden vm" ret yn(hiddenVM);
2462  
    
2463  
    if "vm start date" ret str(vmStarted);
2464  
    
2465  
    if "gc" ret "OK" with timedGC();
2466  
    
2467  
    if "all loaded program jars" ret struct(allLoadedProgramJars());
2468  
    if "all loaded library jars" ret struct(allLoadedLibraryJars());
2469  
    
2470  
    if "stack traces"
2471  
      ret renderAllThreadsWithStackTraces();
2472  
    
2473  
    null;
2474  
  }
2475  
  
2476  
  static void loadMyClasses() {
2477  
    for (S name : classNamesInJarOrDir(javaxJarPath()))
2478  
      pcall-silent { Class.forName(name); }
2479  
  }
2480  
  
2481  
  static synchronized PaA findInjection(S injectionID) {
2482  
    for (PaA paa : injectable_programsInjected)
2483  
      if (eq(paa.injectionID, injectionID))
2484  
        ret paa;
2485  
    ret null;
2486  
  }
2487  
  
2488  
  static new AtomicBoolean readLine_used;
2489  
  static BufferedReader readLine_reader;
2490  
  static bool readLine_noReadLine;
2491  
  sbool quietLoading, verboseIllegal;
2492  
  
2493  
  static synchronized S pollSystemInBuf() {
2494  
    ret systemInBuf.poll();
2495  
  }
2496  
2497  
  static S readLine() {
2498  
    ret readLine(false);
2499  
  }
2500  
  
2501  
  static S readLine(bool quiet) ctex {
2502  
    if (!readLine_used.compareAndSet(false, true))
2503  
      throw fail("readLine is in use.");
2504  
    try {
2505  
      while (true) {
2506  
        S s = pollSystemInBuf();
2507  
        if (s != null) {
2508  
          print(s);
2509  
          ret s;
2510  
        }
2511  
        
2512  
        if (readLine_reader == null)
2513  
          readLine_reader = new BufferedReader(new InputStreamReader(System.in, "UTF-8")); // XX - is that right?
2514  
          
2515  
        if (!readLine_reader.ready())
2516  
          sleep(100);
2517  
        else {
2518  
          s = readLine_reader.readLine();
2519  
          if (s != null) {
2520  
            if (!quiet) print(s);
2521  
            ret s;
2522  
          }
2523  
        }
2524  
      }
2525  
    } finally {
2526  
      readLine_used.set(false);
2527  
    }
2528  
  }
2529  
  
2530  
  static new HashMap<S, WeakReference> weakrefs;
2531  
  // static new WeakIdentityHashMap<O, S> reverseWeakrefs;
2532  
  static long weakRefCounter;
2533  
  
2534  
  // TODO: lookup in reverse map
2535  
  static synchronized S weakref(O o) {
2536  
    if (o == null) ret "null";
2537  
    S id = vmID + "/" + ++weakRefCounter;
2538  
    weakrefs.put(id, new WeakReference(o));
2539  
    ret id;
2540  
  }
2541  
2542  
  static synchronized O getRef(S id) {
2543  
    // TODO: clean up the list some time
2544  
    
2545  
    WeakReference ref = weakrefs.get(id);
2546  
    if (ref == null) ret null;
2547  
    ret ref.get();
2548  
  }
2549  
  
2550  
  static new L<O> multiPorts;
2551  
  
2552  
  static synchronized L<O> getMultiPorts() {
2553  
    ret cloneList(multiPorts);
2554  
  }
2555  
  
2556  
  // true if you're the first one
2557  
  static synchronized boolean addMultiPort(O o) {
2558  
    multiPorts.add(o);
2559  
    /*if (multiPorts.size() == 1) {
2560  
      thread "keep alive" { x30.sleep(); } // keep VM alive since there is a multiport
2561  
    }*/
2562  
    ret multiPorts.size() == 1;
2563  
  }
2564  
  
2565  
  static synchronized void removeMultiPort(O o) {
2566  
    multiPorts.remove(o);
2567  
  }
2568  
  
2569  
  static S getInjectionID(Class mainClass) {
2570  
    L<PaA> l = getInjections();
2571  
    for (PaA injection : l)
2572  
      if (injection.mainClass == mainClass)
2573  
        ret injection.injectionID;
2574  
    ret null;
2575  
  }
2576  
  
2577  
  static S getInjectionID() { return null; } // used somewhere...
2578  
  
2579  
  static void sleep(long ms) {
2580  
    try {
2581  
      Thread.sleep(ms);
2582  
    } catch (Exception e) { throw new RuntimeException(e); }
2583  
  }
2584  
2585  
  static void sleep() ctex {
2586  
    print("Sleeping.");
2587  
    synchronized(x30.class) { x30.class.wait(); }
2588  
  }
2589  
  
2590  
  static Map<Class, S> classToSourceCode = newWeakHashMap();
2591  
  
2592  
  synchronized static void registerSourceCode(Class c, S src) {
2593  
    classToSourceCode.put(c, src);
2594  
  }
2595  
  
2596  
  synchronized static S getSourceCodeForClass(Class c) {
2597  
    ret classToSourceCode.get(c);
2598  
  }
2599  
  
2600  
  static void autoScroll(boolean on) {
2601  
    if (console == null) ret;
2602  
    console.autoScroll = on;
2603  
    if (on) console.scrollToBottomLater();
2604  
  }
2605  
  
2606  
  // get main class of first program (for console)
2607  
  static Class getMainMainClass() {
2608  
    PaA paa = first(getInjections());
2609  
    ret paa == null ? null : paa.mainClass;
2610  
  }
2611  
  
2612  
  // program ID of first program loaded
2613  
  static S mainProgramID() {
2614  
    PaA paa = first(getInjections());
2615  
    ret paa == null ? null : paa.progID;
2616  
  }
2617  
  
2618  
  static bool _inCore() {
2619  
    ret true;
2620  
  }
2621  
  
2622  
  static int cleanKillTimeout = 60000;
2623  
  static volatile bool killing;
2624  
  sbool preKill_verbose = false;
2625  
  sbool sendKillNotice = false;
2626  
  
2627  
  static void preKill() {
2628  
    if (killing) ret;
2629  
    killing = true;
2630  
    directNohupJava_loggingOn = false;
2631  
    final new Flag flag;
2632  
    thread "Cleaning" {
2633  
      try {
2634  
        new HashSet<Class> cleanedUp;
2635  
        if (preKill_verbose) print("Cleaning up injections");
2636  
        for (PaA p : getInjections())
2637  
          if (cleanedUp.add(p.mainClass))
2638  
            preKill_cleanUp(p.mainClass);
2639  
        if (preKill_verbose) print("Cleaning up main classes");
2640  
        for (Class c : allMainClasses())
2641  
          if (cleanedUp.add(c)) preKill_cleanUp(c);
2642  
        if (preKill_verbose) print("Clean-up done");
2643  
      } finally {
2644  
        flag.raise();
2645  
      }
2646  
    }
2647  
    if (sendKillNotice) {
2648  
      if (verbose || preKill_verbose) print("Sending kill notice to vm lister");
2649  
      evalWithTimeout(1.0, r { sendOptQuietly("VM Lister.", "killing vm " + vmPort()) });
2650  
    }
2651  
    if (verbose) print("Waiting for clean kill flag with timeout " + cleanKillTimeout);
2652  
    flag.waitUntilUp(cleanKillTimeout);
2653  
    if (verbose) print("Clean kill flag up (or timeout)");
2654  
  }
2655  
  
2656  
  svoid preKill_cleanUp(O o) {
2657  
    S programID = null;
2658  
    if (preKill_verbose) {
2659  
      programID = (S) getOpt(o, 'programID);
2660  
      print("prekill: Cleaning " + o + " (" + programID + ")");
2661  
    }
2662  
    cleanUp(o);
2663  
    if (preKill_verbose)
2664  
      print("prekill: Cleaned " + programID);
2665  
  }
2666  
  
2667  
  static void cleanRestart(final S[] args) {
2668  
    print("\nClean-restarting myself.");
2669  
    thread "Clean Restart" {
2670  
      preKill();
2671  
      nohupJavax(smartJoin(args), fullVMArguments());
2672  
      System.exit(0);
2673  
    }
2674  
  }
2675  
  
2676  
  sS cleanKillMsg = "\nClean exit\n";
2677  
  
2678  
  svoid cleanKill() {
2679  
    print_noNewLine(cleanKillMsg);
2680  
    thread "Clean Kill" {
2681  
      preKill();
2682  
      System.exit(0);
2683  
    }
2684  
  }
2685  
  
2686  
  static void pauseAll(bool b) {
2687  
    for (PaA injection : getInjections())
2688  
      setOpt(injection.mainClass, "ping_pauseAll", b);
2689  
  }
2690  
  
2691  
  static Class mc() { return x30.class; }
2692  
  static Class getMainClass() { return x30.class; }
2693  
2694  
  static Class getMainClass(O o) ctex {
2695  
    ret (o instanceof Class ? (Class) o : o.getClass()).getClassLoader().loadClass("main");
2696  
  }
2697  
  
2698  
  static class FileAccess {
2699  
    S path;
2700  
    long time;
2701  
    
2702  
    *(S *path) { time = now(); }
2703  
  }
2704  
  
2705  
  static L<FileAccess> lastFilesWritten = synchroList();
2706  
  static int lastFilesWritten_max = 5;
2707  
  
2708  
  static void registerIO(O o, S path, bool forWriting) {
2709  
    if (forWriting)
2710  
      recordWriteAccess(path);
2711  
  }
2712  
  
2713  
  static void recordWriteAccess(S path) {
2714  
    synchronized(lastFilesWritten) {
2715  
      trimListToSizeInFront(lastFilesWritten, lastFilesWritten_max);
2716  
      lastFilesWritten.add(new FileAccess(new File(path).getAbsolutePath()));
2717  
    }
2718  
  }
2719  
  
2720  
  static L<S> lastURLsOpened = synchroList();
2721  
  static int lastURLsOpened_max = 10;
2722  
  static volatile long urlsOpenedCounter;
2723  
  
2724  
  static void recordOpenURLConnection(S url) {
2725  
    url = hideCredentials(url);
2726  
    synchronized(lastURLsOpened) {
2727  
      urlsOpenedCounter++;
2728  
      trimListToSizeInFront(lastURLsOpened, lastURLsOpened_max);
2729  
      lastURLsOpened.add(url);
2730  
    }
2731  
  }
2732  
  
2733  
  static void dropIO(O o) {}
2734  
  
2735  
  // for the "assist" button
2736  
  static Class include(S progID) {
2737  
    Class c = hotwire(progID);
2738  
    setOpt(c, "programID", mainProgramID());
2739  
    ret c;
2740  
  }
2741  
  
2742  
  static void reRunMain {
2743  
    final PaA paa = first(getInjections());
2744  
    if (paa != null) {
2745  
      if (!paa.mainDone) print("Main still running");
2746  
      else {
2747  
        paa.mainDone = false;
2748  
        S progID = paa.progID;
2749  
        thread progID {
2750  
          print();
2751  
          paaRun(paa);
2752  
          System.out.println(paa.exception != null ? "[main done with error]" : "[main done]");
2753  
        }
2754  
      }
2755  
    }
2756  
  }
2757  
  
2758  
  !include #1000963 // hotwire
2759  
  !include #1001372 // nohupJavax
2760  
  
2761  
  volatile sbool consoleUpdateOff;
2762  
  
2763  
  // static Map<Class, L<Thread>> mainThreads = newWeakHashMap();
2764  
  
2765  
  static synchronized PaA preInjectProgram(S progID, S[] arguments) {
2766  
    progID = fsI(progID);
2767  
    PaA paa = new PaA(progID, arguments);
2768  
    paa.injectionID = randomID(8);
2769  
    addInjection(paa);
2770  
2771  
    // better call JavaX for translation in a single thread.
2772  
    paa.mainClass = hotwire(progID);
2773  
    ret paa;
2774  
  }
2775  
  
2776  
  please include function startDeadlockDetector.
2777  
  
2778  
  static Map<Class, Bool> allMainClasses = newWeakHashMap();
2779  
  
2780  
  static void registerAMainClass(Class c) {
2781  
    allMainClasses.put(c, Boolean.TRUE);
2782  
  }
2783  
  
2784  
  static L<Class> allMainClasses() {
2785  
    ret asList(keys(cloneMap(allMainClasses)));
2786  
  }
2787  
  
2788  
  svoid hideVM {
2789  
    hiddenVM = true;
2790  
    sendOpt("VM Lister.", "hiding vm " + vmPort());
2791  
    hideConsole();
2792  
  }
2793  
  
2794  
  static void vmKeep_store(S programID, S programMD5, S var, S structure) {
2795  
    lock vmKeep_lock;
2796  
    programID = fsI(programID);
2797  
    VMKeep data = vmKeep.get(programID);
2798  
    if (data == null || neq(data.programMD5, programMD5))
2799  
      vmKeep.put(programID, data = nu(VMKeep, +programMD5));
2800  
    data.vars.put(var, structure);
2801  
  }
2802  
  
2803  
  static S vmKeep_get(S programID, S programMD5, S var) {
2804  
    lock vmKeep_lock;
2805  
    programID = fsI(programID);
2806  
    VMKeep data = vmKeep.get(programID);
2807  
    if (data == null) null;
2808  
    if (neq(data.programMD5, programMD5)) {
2809  
      vmKeep.remove(programID);
2810  
      null;
2811  
    }
2812  
    ret data.vars.get(var);
2813  
  }
2814  
  
2815  
  static JButton consoleButton(S text) {
2816  
    ret setHorizontalMargin(0, jbutton(text));
2817  
  }
2818  
  
2819  
  // value = true (dangerous) or init function (dangerous) or false (not dangerous)
2820  
  static WeakIdentityHashMap<Map, O> weakMaps;
2821  
  static bool weakMaps_initing, weakMaps_debug;
2822  
  
2823  
  static WeakIdentityHashMap<Cl, O> weakCollections;
2824  
2825  
  static <A extends Map> A _registerWeakMap(A o) {
2826  
    ret _registerWeakMap(o, false);
2827  
  }
2828  
  
2829  
  static <A extends Map> A _registerDangerousWeakMap(A o) {
2830  
    ret _registerWeakMap(o, true);
2831  
  }
2832  
  
2833  
  static <A extends Map> A _registerDangerousWeakMap(A o, O init) {
2834  
    ret _registerWeakMap(o, init == null ? true : init);
2835  
  }
2836  
  
2837  
  static <A extends Map> A _registerWeakMap(A o, O init) {
2838  
    if (weakMaps == null) {
2839  
      if (weakMaps_debug) print("_registerWeakMap init cycle");
2840  
      if (weakMaps_initing) ret o;
2841  
      weakMaps_initing = true;
2842  
      weakMaps = newWeakIdentityHashMap();
2843  
      weakMaps_initing = false;
2844  
    }
2845  
    
2846  
    synchronized(weakMaps) {
2847  
      if (weakMaps_debug) print("_registerWeakMap adding " + getClassName(o) + ", size=" + l(weakMaps));
2848  
      O result = weakMaps.put(o, init);
2849  
      if (weakMaps_debug) print("_registerWeakMap added. " + result + ", size=" + l(weakMaps));
2850  
    }
2851  
    ret o;
2852  
  }
2853  
  
2854  
  static <A extends Cl> A _registerWeakCollection(A o) {
2855  
    if (weakCollections == null) {
2856  
      if (weakMaps_debug) print("_registerWeakCollection init cycle");
2857  
      weakCollections = newWeakIdentityHashMap();
2858  
    }
2859  
    
2860  
    synchronized(weakCollections) {
2861  
      if (weakMaps_debug) print("_registerWeakCollection adding " + getClassName(o) + ", size=" + l(weakCollections));
2862  
      // there are dangerous weak maps (when a value hard-references
2863  
      // its key), but there are no dangerous weak collections.
2864  
      // so the value here is always "false"
2865  
      O result = weakCollections.put(o, false);
2866  
      if (weakMaps_debug) print("_registerWeakCollection added. " + result + ", size=" + l(weakCollections));
2867  
    }
2868  
    ret o;
2869  
  }
2870  
  
2871  
  static void cleanWeakMaps() { pcall {
2872  
    cleanWeakCollections();
2873  
    
2874  
    new L<Map> maps;
2875  
    new Map<Map, O> dangerousMaps;
2876  
    if (weakMaps == null) ret;
2877  
    synchronized(weakMaps) {
2878  
      for (Map map : keys(weakMaps)) { // This cleans the weakMaps map itself
2879  
        O init = weakMaps.get(map);
2880  
        if (eq(init, false)) maps.add(map);
2881  
        else dangerousMaps.put(map, init);
2882  
      }
2883  
      if (weakMaps_debug) print("cleanWeakMaps: got " + l(maps));
2884  
    }
2885  
    for (Map o : maps) pcall { cleanWeakMap(o); }
2886  
    for (Map o : keys(dangerousMaps)) pcall {
2887  
      synchronized(o) {
2888  
        o.clear();
2889  
        O init = dangerousMaps.get(o);
2890  
        //print("Calling init on dangerous map: " + init + " / " + className(o));
2891  
        if (!init instanceof Bool)
2892  
          callF(init, o);
2893  
      }
2894  
    }
2895  
  }}
2896  
  
2897  
  static void cleanWeakCollections() { pcall {
2898  
    new L<Cl> collections;
2899  
    if (weakCollections == null) ret;
2900  
    synchronized(weakCollections) {
2901  
      for (Cl cl : keys(weakCollections)) { // This cleans the weakCollections map itself
2902  
        O init = weakCollections.get(cl);
2903  
        collections.add(cl);
2904  
      }
2905  
      if (weakMaps_debug) print("cleanWeakCollections: got " + l(collections));
2906  
    }
2907  
    for (Cl o : collections) pcall { cleanWeakCollection(o); }
2908  
  }}
2909  
  
2910  
  svoid setVarDialog {
2911  
    final Class c = getMainMainClass();
2912  
    final JComboBox cb = jcombobox(staticFieldNames(c));
2913  
    final JTextField tf = jtextfield();
2914  
    showFormTitled("Set variable",
2915  
      "Name", cb,
2916  
      "Value", tf, func {
2917  
      try {
2918  
        S var = getSelectedItem(cb), value = getText(tf);
2919  
        set(c, var, unstructure(value));
2920  
        infoBox("Variable " + var + " set to " + value + "!");
2921  
      } catch e {
2922  
        messageBox(e);
2923  
        false;
2924  
      }
2925  
      null;
2926  
    });
2927  
  }
2928  
  
2929  
  please include function findTranslators. // 1006722 calls this
2930  
  
2931  
  // VM-wide FAST string interning (need to expose to programs per interface for more speed)
2932  
  please include function internPerProgram.
2933  
  
2934  
  please include function setGCFrequency.
2935  
  please include function noRegularGC.
2936  
  
2937  
  please include function myTranspilationDate.
2938  
2939  
  // for programs:
2940  
  please include function newWeakHashMap.
2941  
  please include function print_append.
2942  
  please include function get.
2943  
  please include function getOpt.
2944  
  please include function callF.
2945  
  please include function call.
2946  
  please include function fixNewLines.
2947  
  
2948  
  !include once #1016423 // _handleError (core version)
2949  
  
2950  
  //please include function substance.
2951  
  
2952  
  static Thread _registerThread(Thread t) { ret t; }
2953  
  svoid _registerThread() {}
2954  
  static Thread _unregisterThread(Thread t) { ret t; }
2955  
  svoid _unregisterThread() {}
2956  
  static Map<Thread, Bool> _registerThread_threads;
2957  
  
2958  
  svoid failIfUnlicensed() {}
2959  
2960  
  sS _showConsole() {  
2961  
    if (console == null) tryToOpenConsole(new S[0]);
2962  
    if (console != null) {
2963  
      console.showConsole();
2964  
      ret "ok";
2965  
    }
2966  
    ret "no console";
2967  
  }
2968  
  
2969  
  please include function loadNativeLibrary.
2970  
  
2971  
  static Map vm_generalMap() { ret generalMap; }
2972  
  
2973  
  // can't do newPing stuff in x30 because we can't see x30_pkg.x30_util.BetterThreadLocal
2974  
  //static BetterThreadLocal<Runnable> newPing_actionTL() { null; }
2975  
  svoid newPing() {}
2976  
} // end of class x30

Author comment

Began life as a copy of #1001600

download  show line numbers  debug dex  old transpilations   

Travelled to 21 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, ddnzoavkxhuk, gwrvuhgaqvyk, irmadwmeruwu, ishqpsrjomds, lpdgvwnxivlt, mowyntqkapby, mqqgnosmbjvj, nysugtttblhj, omdjrrnzbjjv, onxytkatvevr, podlckwnjdmb, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv

No comments. add comment

Snippet ID: #1001638
Snippet name: x30.java (JavaX) - packaged by #1001639
Eternal ID of this version: #1001638/386
Text MD5: 0e1b94cbaa813dc16c46888196fa851c
Transpilation MD5: 24a534ac84a7158938e8962fe675085d
Author: stefan
Category: javax
Type: JavaX module (desktop)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2022-01-04 20:19:14
Source code size: 100825 bytes / 2976 lines
Pitched / IR pitched: No / No
Views / Downloads: 3376 / 17994
Version history: 385 change(s)
Referenced in: [show references]