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

5318
LINES

< > BotCompany Repo | #1004182 // x30 for Android [stage 3, Java, LIVE]

JavaX source code (Android) [tags: use-pretranspiled] - run with: the app

Libraryless. Click here for Pure Java version (5392L/38K/114K).

1  
import java.util.*;
2  
import java.util.zip.*;
3  
import java.util.List;
4  
import java.util.regex.*;
5  
import java.util.concurrent.*;
6  
import java.util.concurrent.atomic.*;
7  
import java.io.*;
8  
import java.net.*;
9  
import java.lang.reflect.*;
10  
import java.lang.ref.*;
11  
import java.lang.management.*;
12  
import java.security.*;
13  
import java.security.spec.*;
14  
import java.math.*;
15  
16  
import android.widget.*;
17  
import android.view.*;
18  
import android.view.View;
19  
import android.content.Context;
20  
import android.app.Activity;
21  
import android.view.inputmethod.*;
22  
import android.content.*;
23  
import android.text.*;
24  
25  
public class main {} // dummy
26  
27  
class x30 implements Runnable {
28  
  static final String version = "JavaX 30";
29  
  static final int subversion = 1;
30  
  static final String javaxProgramID = "#1001638";
31  
  
32  
  static Class bootUpClass;
33  
  
34  
  static ThreadLocal onProgramLoad = new ThreadLocal();
35  
  
36  
  // If programs run longer than this, they might have their class files
37  
  // deleted. One day for now.
38  
  static int tempFileRetentionTime = 1*24; // hours
39  
  
40  
  static int maxConsoleChars = 1024*1024;
41  
  
42  
  static boolean verbose = false, translate = false, list = false, virtualizeTranslators = true;
43  
  static String translateTo = null;
44  
  static boolean preferCached = false, noID = false, noPrefetch = false, noAWT = false;
45  
  static boolean safeOnly = false, safeTranslate = false, javacOnly = false, logOn = true;
46  
  static boolean runMainInProcess = true, consoleOn = true, hasHelloMessage = false;
47  
  static List<String[]> mainTranslators = new ArrayList<String[]>();
48  
  private static Map<Long, String> memSnippetCache = new HashMap<Long, String>();
49  
  private static int processesStarted, compilations;
50  
51  
  // snippet ID -> md5
52  
  private static HashMap<Long, String> prefetched = new HashMap<Long, String>();
53  
  private static File virtCache;
54  
  
55  
  // doesn't work yet
56  
  private static Map<String, Class<?>> programCache = new HashMap<String, Class<?>>();
57  
  static boolean cacheTranslators = false;
58  
59  
  // this should work (caches transpiled translators)
60  
  private static HashMap<Long, Object[]> translationCache = new HashMap<Long, Object[]>();
61  
  static boolean cacheTranspiledTranslators = true;
62  
63  
  // which snippets are available pre-transpiled server-side?
64  
  private static Set<Long> hasTranspiledSet = new HashSet<Long>();
65  
  static boolean useServerTranspiled = true;
66  
67  
  static Object androidContext;
68  
  static boolean android = isAndroid();
69  
  
70  
  // We stick to 1.7 for now to support android.
71  
  // Scripts like #1001155 might change to 1.6
72  
  static String javaTarget = System.getProperty("java.version").startsWith("1.6.") ? "1.6" : "1.7";
73  
74  
  // Translators currently being translated (to detect recursions)
75  
  private static Set<Long> translating = new HashSet<Long>();
76  
77  
  static String lastOutput;
78  
  static String[] fullArgs;
79  
  private static Console console;
80  
  
81  
  static String javaCompilerOutput;
82  
  
83  
  static int systemOutPipeSize = 128*1024; // 128 K
84  
  static int systemErrPipeSize = 4*1024; // 4 K
85  
  static int systemInPipeSize = 4*1024; // 4 K
86  
  
87  
  // store anything here
88  
  static Map generalMap = synchroHashMap();
89  
90  
  public static void main(String[] args) {
91  
    try {
92  
      goMain(args);
93  
    } catch (Throwable e) {
94  
      e.printStackTrace();
95  
    }
96  
  }
97  
  
98  
  static void goMain(String[] args) throws Exception {
99  
    __javax = x30.class; // for hotwire
100  
    
101  
    if (args.length != 0 && args[0].equals("-v")) verbose = true;
102  
103  
    for (String arg : args)
104  
      if (arg.equals("-noawt"))
105  
        noAWT = true;
106  
        
107  
    if (!hasHelloMessage) {
108  
      hasHelloMessage = true;
109  
      //installHelloMessage(args.length == 0 ? "JavaX Start-Up VM" : "JavaX VM (" + smartJoin(args) + ")");
110  
      makeVMAndroid();
111  
    }
112  
    
113  
    File ioBaseDir = new File("."), inputDir = null, outputDir = null;
114  
    String src = null;
115  
    List<String> programArgs = new ArrayList<String>();
116  
    fullArgs = args;
117  
118  
    for (int i = 0; i < args.length; i++) {
119  
      String arg = args[i];
120  
121  
      if (arg.equals("-version")) {
122  
        showVersion();
123  
        System.exit(0);
124  
      }
125  
126  
      if (arg.equals("-sysprop")) {
127  
        showSystemProperties();
128  
        return;
129  
      }
130  
131  
      if (arg.equals("-v") || arg.equals("-verbose"))
132  
        verbose = true;
133  
      else if (arg.equals("-finderror"))
134  
        verbose = true;
135  
      else if (arg.equals("-offline") || arg.equalsIgnoreCase("-prefercached"))
136  
        preferCached = true;
137  
      else if (arg.equals("-novirt"))
138  
        virtualizeTranslators = false;
139  
      else if (arg.equals("-safeonly"))
140  
        safeOnly = true;
141  
      else if (arg.equals("-safetranslate"))
142  
        safeTranslate = true;
143  
      else if (arg.equals("-noawt"))
144  
        noAWT = true;
145  
      else if (arg.equals("-noid"))
146  
        noID = true;
147  
      else if (arg.equals("-nocachetranspiled"))
148  
        cacheTranspiledTranslators = false;
149  
      else if (arg.equals("-javac"))
150  
        javacOnly = true;
151  
      else if (arg.equals("-localtranspile"))
152  
        useServerTranspiled = false;
153  
      else if (arg.equals("translate") && src == null)
154  
        translate = true;
155  
      else if (arg.equals("list") && src == null) {
156  
        list = true;
157  
        virtualizeTranslators = false; // so they are silenced
158  
      } else if (arg.equals("run") && src == null) {
159  
        // it's the default command anyway
160  
      } else if (arg.startsWith("input="))
161  
        inputDir = new File(arg.substring(6));
162  
      else if (arg.startsWith("output="))
163  
        outputDir = new File(arg.substring(7));
164  
      else if (arg.equals("with"))
165  
        mainTranslators.add(new String[] {args[++i], null});
166  
      else if (translate && arg.equals("to"))
167  
        translateTo = args[++i];
168  
      else if (src == null) {
169  
        //System.out.println("src=" + arg);
170  
        src = arg;
171  
      } else
172  
        programArgs.add(arg);
173  
    }
174  
175  
    cleanCache();
176  
177  
    if (useServerTranspiled)
178  
      noPrefetch = true;
179  
180  
    if (src == null) src = ".";
181  
182  
    // Might actually want to write to 2 disk caches (global/per program).
183  
    if (virtualizeTranslators && !preferCached)
184  
      virtCache = TempDirMaker_make();
185  
186  
    if (inputDir != null) {
187  
      ioBaseDir = TempDirMaker_make();
188  
      System.out.println("Taking input from: " + inputDir.getAbsolutePath());
189  
      System.out.println("Output is in: " + new File(ioBaseDir, "output").getAbsolutePath());
190  
      copyInput(inputDir, new File(ioBaseDir, "input"));
191  
    }
192  
    
193  
    if (logOn)
194  
      logStart(args);
195  
196  
    javaxmain(src, ioBaseDir, translate, list, programArgs.toArray(new String[programArgs.size()]));
197  
198  
    if (outputDir != null) {
199  
      copyInput(new File(ioBaseDir, "output"), outputDir);
200  
      System.out.println("Output copied to: " + outputDir.getAbsolutePath());
201  
    }
202  
203  
    if (verbose) {
204  
      // print stats
205  
      System.out.println("Processes started: " + processesStarted + ", compilations: " + compilations);
206  
    }
207  
  }
208  
209  
static void saveTranspiledCode(String progID, String code) {
210  
  saveTextFile(new File(getCodeProgramDir(progID), "Transpilation"), code);
211  
}
212  
213  
static File getCodeProgramDir() {
214  
  return getCodeProgramDir(getProgramID());
215  
}
216  
217  
static File getCodeProgramDir(String snippetID) {
218  
  return new File(userHome(), "JavaX-Code/" + formatSnippetID(snippetID));
219  
}
220  
221  
static File getCodeProgramDir(long snippetID) {
222  
  return getCodeProgramDir(formatSnippetID(snippetID));
223  
}
224  
225  
public static void javaxmain(String src, File ioDir, boolean translate, boolean list,
226  
                               String[] args) throws Exception {
227  
    String programID = isSnippetID(src) ? "" + parseSnippetID(src) : null;
228  
    
229  
    if (programID != null)
230  
      System.err.println("JavaX TRANSLATE " + programID + " " + smartJoin(args));
231  
    
232  
    List<File> libraries = new ArrayList<File>();
233  
    File X = transpileMain(src, libraries);
234  
    if (verbose)
235  
      print("After transpileMain: " + X);
236  
      
237  
    if (X == null) {
238  
      showVersion();
239  
      
240  
      if (fullArgs != null) {
241  
        String[] nargs;
242  
        if (fullArgs.length == 0)
243  
          nargs = new String[] {"1000825"}; // swing-start
244  
        else {
245  
          // forward to search
246  
          nargs = new String[fullArgs.length+1];
247  
          nargs[0] = "636";
248  
          // nargs[1] = "search-runnables";
249  
          System.arraycopy(fullArgs, 0, nargs, 1, fullArgs.length);
250  
        }
251  
        main(nargs); // Hopefully we get no infinite recursion :)
252  
        return;
253  
      }
254  
      
255  
      System.out.println("No main.java found, exiting");
256  
      return;
257  
    }
258  
    
259  
    info.transpiledSrc = X;
260  
261  
    // list or run
262  
263  
    if (translate) {
264  
      File to = X;
265  
      if (translateTo != null) {
266  
        StringBuilder buf = new StringBuilder();
267  
        for (File f : libraries) buf.append(f.getName()+"\n");
268  
        if (new File(translateTo).isDirectory()) {
269  
          to = new File(translateTo, "main.java");
270  
          saveTextFile(new File(translateTo, "libraries.txt").getPath(), buf.toString());
271  
        } else {
272  
          to = new File(translateTo);
273  
          saveTextFile(new File(translateTo + "_libraries").getPath(), buf.toString());
274  
        }
275  
      }
276  
      if (to != X)
277  
        copy(new File(X, "main.java"), to);
278  
      System.out.println("Program translated to: " + to.getAbsolutePath());
279  
    } else if (list)
280  
      System.out.println(loadTextFile(new File(X, "main.java").getPath(), null));
281  
    else {
282  
      if (programID != null)
283  
        System.err.println("JavaX RUN " + programID + " " + smartJoin(args));
284  
      System.err.println(); // Make empty line before actual program starts
285  
      
286  
      Class mainClass = javax2(X, ioDir, false, runMainInProcess, libraries, args, null, programID, info);
287  
      
288  
      if (!isTrue(getOpt(mainClass, "mainDoneQuietly_on")))
289  
        System.out.println("[main done]");
290  
    }
291  
  }
292  
293  
static File transpileMain(String src, List<File> libraries) throws Exception {
294  
    File srcDir = null;
295  
    boolean isTranspiled = false;
296  
    if (isSnippetID(src)) {
297  
      String transpiledSrc = getTranspilationFromBossBot(parseSnippetID(src));
298  
      if (transpiledSrc != null) {
299  
        int i = transpiledSrc.indexOf('\n');
300  
        String libs = transpiledSrc.substring(0, Math.max(0, i));
301  
        transpiledSrc = transpiledSrc.substring(i+1);
302  
        if (!transpiledSrc.isEmpty()) {
303  
          srcDir = TempDirMaker_make();
304  
          saveTextFile(new File(srcDir, "main.java").getPath(), transpiledSrc);
305  
          isTranspiled = true;
306  
307  
          Matcher m = Pattern.compile("\\d+").matcher(libs);
308  
          while (m.find()) {
309  
            String libid = m.group();
310  
            File libraryFile = DiskSnippetCache_getLibrary(parseSnippetID(libid));
311  
            loadLibrary(libid, libraries, libraryFile);
312  
          }
313  
        }
314  
      }
315  
     
316  
      if (srcDir == null) {
317  
        prefetch(src);
318  
        long id = parseSnippetID(src);
319  
        prefetched.remove(id); // hackfix to ensure transpiled main program is found.
320  
        srcDir = loadSnippetAsMainJava(src);
321  
        if (verbose)
322  
          System.err.println("hasTranspiledSet: " + hasTranspiledSet);
323  
        if (hasTranspiledSet.contains(id) && useServerTranspiled) {
324  
          //System.err.println("Trying pretranspiled main program: #" + id);
325  
          transpiledSrc = getServerTranspiled2("#" + id);
326  
          int i = transpiledSrc.indexOf('\n');
327  
          String libs = transpiledSrc.substring(0, Math.max(0, i));
328  
          transpiledSrc = transpiledSrc.substring(i+1);
329  
          if (!transpiledSrc.isEmpty()) {
330  
            srcDir = TempDirMaker_make();
331  
            saveTextFile(new File(srcDir, "main.java").getPath(), transpiledSrc);
332  
            isTranspiled = true;
333  
            //translationCache.put(id, new Object[] {srcDir, libraries});
334  
  
335  
            Matcher m = Pattern.compile("\\d+").matcher(libs);
336  
            while (m.find()) {
337  
              String libid = m.group();
338  
              File libraryFile = DiskSnippetCache_getLibrary(parseSnippetID(libid));
339  
              loadLibrary(libid, libraries, libraryFile);
340  
            }
341  
          }
342  
        }
343  
      }
344  
    } else {
345  
      srcDir = new File(src);
346  
347  
      // if the argument is a file, it is assumed to be main.java
348  
      if (srcDir.isFile()) {
349  
        srcDir = TempDirMaker_make();
350  
        copy(new File(src), new File(srcDir, "main.java"));
351  
      }
352  
353  
      if (!new File(srcDir, "main.java").exists())
354  
        return null;
355  
    }
356  
357  
    // translate
358  
359  
    File X = srcDir;
360  
361  
    if (!isTranspiled) {
362  
      X = topLevelTranslate(X, libraries);
363  
      System.err.println("Translated " + src);
364  
365  
      // save prefetch data
366  
      if (isSnippetID(src))
367  
        savePrefetchData(src);
368  
    }
369  
    return X;
370  
  }
371  
  
372  
 private static void prefetch(String mainSnippetID) throws IOException {
373  
    if (noPrefetch) return;
374  
375  
    long mainID = parseSnippetID(mainSnippetID);
376  
    String s = mainID + " " + loadTextFile(new File(userHome(), ".tinybrain/prefetch/" + mainID + ".txt").getPath(), "");
377  
    String[] ids = s.trim().split(" ");
378  
    if (ids.length > 1) {
379  
      String url = tb_mainServer() + "/tb-int/prefetch.php?ids=" + URLEncoder.encode(s, "UTF-8") + standardCredentials();
380  
      String data = loadPage(new URL(url));
381  
      String[] split = data.split(" ");
382  
      if (split.length == ids.length)
383  
        for (int i = 0; i < ids.length; i++)
384  
          prefetched.put(parseSnippetID(ids[i]), split[i]);
385  
    }
386  
  }
387  
  
388  
static String userHomeInternal() {
389  
  return ((File) call(androidContext, "getFilesDir")).getAbsolutePath();
390  
}
391  
392  
static String _userHome;
393  
static String userHome() {
394  
  if (_userHome == null) {
395  
    if (isAndroid())
396  
      _userHome = "/storage/emulated/0/";
397  
    else
398  
      _userHome = System.getProperty("user.home");
399  
    //System.out.println("userHome: " + _userHome);
400  
  }
401  
  return _userHome;
402  
}
403  
404  
  private static void savePrefetchData(String mainSnippetID) throws IOException {
405  
    List<String> ids = new ArrayList<String>();
406  
    long mainID = parseSnippetID(mainSnippetID);
407  
408  
    for (long id : memSnippetCache.keySet())
409  
      if (id != mainID)
410  
        ids.add(String.valueOf(id));
411  
412  
    saveTextFile(new File(userHome(),".tinybrain/prefetch/" + mainID + ".txt").getPath(), join(" ", ids));
413  
  }
414  
415  
  static File topLevelTranslate(File srcDir, List<File> libraries_out) throws Exception {
416  
    File X = srcDir;
417  
    X = applyTranslators(X, mainTranslators, libraries_out); // translators supplied on command line (unusual)
418  
419  
    // actual inner translation of the JavaX source
420  
    X = defaultTranslate(X, libraries_out);
421  
    return X;
422  
  }
423  
424  
  private static File defaultTranslate(File x, List<File> libraries_out) throws Exception {
425  
    x = luaPrintToJavaPrint(x);
426  
    x = repeatAutoTranslate(x, libraries_out);
427  
    return x;
428  
  }
429  
430  
  private static File repeatAutoTranslate(File x, List<File> libraries_out) throws Exception {
431  
    List<String[]> postTranslators = new ArrayList<String[]>();
432  
    
433  
    while (true) {
434  
      String main = loadTextFile(new File(x, "main.java").getPath(), null);
435  
      List<String> lines = toLines(main);
436  
      List<String[]> t = findPostTranslators(lines);
437  
      postTranslators.addAll(t);
438  
      
439  
      if (!t.isEmpty()) {
440  
        main = fromLines(lines);
441  
        x = TempDirMaker_make();
442  
        saveTextFile(new File(x, "main.java").getPath(), main);
443  
      }
444  
445  
      File y = autoTranslate(x, libraries_out);
446  
      if (y == x)
447  
        break;
448  
      x = y;
449  
    }
450  
    
451  
    x = applyTranslators(x, postTranslators, libraries_out);
452  
    
453  
    return x;
454  
  }
455  
456  
  private static File autoTranslate(File x, List<File> libraries_out) throws Exception {
457  
    String main = loadTextFile(new File(x, "main.java").getPath(), null);
458  
    List<String> lines = toLines(main);
459  
    List<String[]> translators = findTranslators(lines);
460  
    if (translators.isEmpty())
461  
      return x;
462  
463  
    main = fromLines(lines);
464  
    File newDir = TempDirMaker_make();
465  
    saveTextFile(new File(newDir, "main.java").getPath(), main);
466  
    return applyTranslators(newDir, translators, libraries_out);
467  
  }
468  
469  
  static List<String[]> findTranslators(List<String> lines) {
470  
    List<String[]> translators = new ArrayList<String[]>();
471  
    Pattern pattern = Pattern.compile("^!([0-9# \t]+)");
472  
    Pattern pArgs = Pattern.compile("^\\s*\\((.*)\\)");
473  
    for (ListIterator<String> iterator = lines.listIterator(); iterator.hasNext(); ) {
474  
      String line = iterator.next();
475  
      line = line.trim();
476  
      Matcher matcher = pattern.matcher(line);
477  
      if (matcher.find()) {
478  
        String[] t = matcher.group(1).split("[ \t]+");
479  
        String rest = line.substring(matcher.end());
480  
        String arg = null;
481  
        if (t.length == 1) {
482  
          Matcher mArgs = pArgs.matcher(rest);
483  
          if (mArgs.find())
484  
            arg = mArgs.group(1);
485  
        }
486  
        for (String transi : t)
487  
          translators.add(new String[]{transi, arg});
488  
        iterator.remove();
489  
      }
490  
    }
491  
    return translators;
492  
  }
493  
494  
  static List<String[]> findPostTranslators(List<String> lines) {
495  
    List<String[]> translators = new ArrayList<String[]>();
496  
    Pattern pattern = Pattern.compile("^!post\\s*([0-9# \t]+)");
497  
    Pattern pArgs = Pattern.compile("^\\s*\\((.*)\\)");
498  
    for (ListIterator<String> iterator = lines.listIterator(); iterator.hasNext(); ) {
499  
      String line = iterator.next();
500  
      line = line.trim();
501  
      Matcher matcher = pattern.matcher(line);
502  
      if (matcher.find()) {
503  
        String[] t = matcher.group(1).split("[ \t]+");
504  
        String rest = line.substring(matcher.end());
505  
        String arg = null;
506  
        if (t.length == 1) {
507  
          Matcher mArgs = pArgs.matcher(rest);
508  
          if (mArgs.find())
509  
            arg = mArgs.group(1);
510  
        }
511  
        for (String transi : t)
512  
          translators.add(new String[]{transi, arg});
513  
        iterator.remove();
514  
      }
515  
    }
516  
    return translators;
517  
  }
518  
519  
  public static List<String> toLines(String s) {
520  
    List<String> lines = new ArrayList<String>();
521  
    int start = 0;
522  
    while (true) {
523  
      int i = toLines_nextLineBreak(s, start);
524  
      if (i < 0) {
525  
        if (s.length() > start) lines.add(s.substring(start));
526  
        break;
527  
      }
528  
529  
      lines.add(s.substring(start, i));
530  
      if (s.charAt(i) == '\r' && i+1 < s.length() && s.charAt(i+1) == '\n')
531  
        i += 2;
532  
      else
533  
        ++i;
534  
535  
      start = i;
536  
    }
537  
    return lines;
538  
  }
539  
540  
  private static int toLines_nextLineBreak(String s, int start) {
541  
    for (int i = start; i < s.length(); i++) {
542  
      char c = s.charAt(i);
543  
      if (c == '\r' || c == '\n')
544  
        return i;
545  
    }
546  
    return -1;
547  
  }
548  
549  
  public static String fromLines(List<String> lines) {
550  
    StringBuilder buf = new StringBuilder();
551  
    for (String line : lines) {
552  
      buf.append(line).append('\n');
553  
    }
554  
    return buf.toString();
555  
  }
556  
557  
  private static File applyTranslators(File x, List<String[]> translators, List<File> libraries_out) throws Exception {
558  
    for (String[] translator : translators)
559  
      x = applyTranslator(x, translator[0], translator[1], libraries_out);
560  
    return x;
561  
  }
562  
563  
  // also takes a library
564  
  private static File applyTranslator(File x, String translator, String arg, List<File> libraries_out) throws Exception {
565  
    if (verbose)
566  
      System.out.println("Using translator " + translator + " on sources in " + x.getPath());
567  
568  
    File newDir = runTranslatorOnInput(translator, null, arg, x, !verbose, libraries_out);
569  
570  
    if (!new File(newDir, "main.java").exists()) {
571  
      throw new Exception("Translator " + translator + " did not generate main.java");
572  
      // TODO: show translator output
573  
    }
574  
    if (verbose)
575  
      System.out.println("Translated with " + translator + " from " + x.getPath() + " to " + newDir.getPath());
576  
    x = newDir;
577  
    return x;
578  
  }
579  
580  
  private static File luaPrintToJavaPrint(File x) throws IOException {
581  
    File newDir = TempDirMaker_make();
582  
    String code = loadTextFile(new File(x, "main.java").getPath(), null);
583  
    code = luaPrintToJavaPrint(code);
584  
    saveTextFile(new File(newDir, "main.java").getPath(), code);
585  
    return newDir;
586  
  }
587  
588  
  public static String luaPrintToJavaPrint(String code) {
589  
    return ("\n" + code).replaceAll(
590  
      "(\n\\s*)print (\".*\")",
591  
      "$1System.out.println($2);").substring(1);
592  
  }
593  
594  
  public static File loadSnippetAsMainJava(String snippetID) throws IOException {
595  
    checkProgramSafety(snippetID);
596  
    File srcDir = TempDirMaker_make();
597  
    saveTextFile(new File(srcDir, "main.java").getPath(), loadSnippet(snippetID));
598  
    return srcDir;
599  
  }
600  
601  
  public static File loadSnippetAsMainJavaVerified(String snippetID, String hash) throws IOException {
602  
    checkProgramSafety(snippetID);
603  
    File srcDir = TempDirMaker_make();
604  
    saveTextFile(new File(srcDir, "main.java").getPath(), loadSnippetVerified(snippetID, hash));
605  
    return srcDir;
606  
  }
607  
608  
  @SuppressWarnings( "unchecked" )
609  
  /** returns output dir */
610  
  private static File runTranslatorOnInput(String snippetID, String hash, String arg, File input,
611  
                                           boolean silent,
612  
                                           List<File> libraries_out) throws Exception {
613  
    if (safeTranslate)
614  
      checkProgramSafetyImpl(snippetID);
615  
    long id = parseSnippetID(snippetID);
616  
617  
    // It's a library, not a translator.
618  
    File libraryFile = DiskSnippetCache_getLibrary(id);
619  
    if (verbose)
620  
      System.out.println("Library file for " + id + ": " + libraryFile);
621  
    if (libraryFile != null) {
622  
      loadLibrary(snippetID, libraries_out, libraryFile);
623  
      return input;
624  
    }
625  
626  
    String[] args = arg != null ? new String[]{arg} : new String[0];
627  
628  
    File srcDir = hash == null ? loadSnippetAsMainJava(snippetID)
629  
      : loadSnippetAsMainJavaVerified(snippetID, hash);
630  
    long mainJavaSize = new File(srcDir, "main.java").length();
631  
632  
    if (verbose)
633  
      System.out.println(snippetID + ": length = " + mainJavaSize);
634  
    if (mainJavaSize == 0) { // no text in snippet? assume it's a library
635  
      loadLibrary(snippetID, libraries_out, libraryFile);
636  
      return input;
637  
    }
638  
639  
    List<File> libraries = new ArrayList<File>();
640  
    Object[] cached = translationCache.get(id);
641  
    if (cached != null) {
642  
      //System.err.println("Taking translator " + snippetID + " from cache!");
643  
      srcDir = (File) cached[0];
644  
      libraries = (List<File>) cached[1];
645  
    } else if (hasTranspiledSet.contains(id) && useServerTranspiled) {
646  
      System.err.println("Trying pretranspiled translator: #" + snippetID);
647  
      String transpiledSrc = getServerTranspiled(snippetID);
648  
      transpiledSrc = transpiledSrc.substring(transpiledSrc.indexOf('\n')+1);
649  
      // TODO: check for libraries
650  
      if (!transpiledSrc.isEmpty()) {
651  
        srcDir = TempDirMaker_make();
652  
        saveTextFile(new File(srcDir, "main.java").getPath(), transpiledSrc);
653  
        translationCache.put(id, cached = new Object[] {srcDir, libraries});
654  
      }
655  
    }
656  
657  
    File ioBaseDir = TempDirMaker_make();
658  
659  
    /*Class<?> mainClass = programCache.get("" + parseSnippetID(snippetID));
660  
    if (mainClass != null)
661  
      return runCached(ioBaseDir, input, args);*/
662  
    // Doesn't work yet because virtualized directories are hardcoded in translator...
663  
664  
    if (cached == null) {
665  
      System.err.println("Translating translator #" + id);
666  
      if (translating.contains(id))
667  
        throw new RuntimeException("Recursive translator reference chain: " + structure(translating));
668  
      translating.add(id);
669  
      try {
670  
        srcDir = defaultTranslate(srcDir, libraries);
671  
      } finally {
672  
        translating.remove(id);
673  
      }
674  
      System.err.println("Translated translator #" + id);
675  
      translationCache.put(id, new Object[]{srcDir, libraries});
676  
    }
677  
678  
    boolean runInProcess = false;
679  
680  
    if (virtualizeTranslators) {
681  
      if (verbose) System.out.println("Virtualizing translator");
682  
683  
      // TODO: don't virtualize class _javax (as included in, say, #636)
684  
685  
      //srcDir = applyTranslator(srcDir, "#2000351"); // I/O-virtualize the translator
686  
      // that doesn't work because it recurses infinitely...
687  
688  
      // So we do it right here:
689  
      String s = loadTextFile(new File(srcDir, "main.java").getPath(), null);
690  
      s = s.replaceAll("new\\s+File\\(", "virtual.newFile(");
691  
      s = s.replaceAll("new\\s+FileInputStream\\(", "virtual.newFileInputStream(");
692  
      s = s.replaceAll("new\\s+FileOutputStream\\(", "virtual.newFileOutputStream(");
693  
      s += "\n\n" + loadSnippet("#2000355"); // load class virtual
694  
695  
      // forward snippet cache (virtualized one)
696  
      File dir = virtCache != null ? virtCache : DiskSnippetCache_dir;
697  
      s = s.replace("static File DiskSnippetCache_dir" + ";",
698  
        "static File DiskSnippetCache_dir " + "= new File(" + javaQuote(dir.getAbsolutePath()) + ");"); // extra + is necessary for Dumb TinyBrain :)
699  
      s = s.replace("static boolean preferCached = false;", "static boolean preferCached = true;");
700  
701  
      if (verbose) {
702  
        System.out.println("==BEGIN VIRTUALIZED TRANSLATOR==");
703  
        System.out.println(s);
704  
        System.out.println("==END VIRTUALIZED TRANSLATOR==");
705  
      }
706  
      srcDir = TempDirMaker_make();
707  
      saveTextFile(new File(srcDir, "main.java").getPath(), s);
708  
709  
      // TODO: silence translator also
710  
      runInProcess = true;
711  
    }
712  
713  
    return runJavaX(ioBaseDir, srcDir, input, silent, runInProcess, libraries,
714  
      args, cacheTranslators ? "" + id : null, "" + id);
715  
  }
716  
717  
  static void checkProgramSafety(String snippetID) throws IOException {
718  
    if (!safeOnly) return;
719  
    checkProgramSafetyImpl(snippetID);
720  
  }
721  
722  
static void checkProgramSafetyImpl(String snippetID) throws IOException {
723  
    URL url = new URL(tb_mainServer() + "/tb-int/is-javax-safe.php?id=" + parseSnippetID(snippetID) + standardCredentials());
724  
    String text = loadPage(url);
725  
    if (!text.startsWith("{\"safe\":\"1\"}"))
726  
      throw new RuntimeException("Program not safe: #" + parseSnippetID(snippetID));
727  
  }
728  
729  
  static void loadLibrary(String snippetID, List<File> libraries_out, File libraryFile) throws IOException {
730  
    if (verbose)
731  
      System.out.println("Assuming " + snippetID + " is a library.");
732  
733  
    if (libraryFile == null) {
734  
      byte[] data = loadDataSnippetImpl(snippetID);
735  
      DiskSnippetCache_putLibrary(parseSnippetID(snippetID), data);
736  
      libraryFile = DiskSnippetCache_getLibrary(parseSnippetID(snippetID));
737  
    }
738  
739  
    if (!libraries_out.contains(libraryFile))
740  
      libraries_out.add(libraryFile);
741  
  }
742  
743  
  /** returns output dir */
744  
  private static File runJavaX(File ioBaseDir, File originalSrcDir, File originalInput,
745  
                               boolean silent, boolean runInProcess,
746  
                               List<File> libraries, String[] args, String cacheAs,
747  
                               String programID) throws Exception {
748  
    File srcDir = new File(ioBaseDir, "src");
749  
    File inputDir = new File(ioBaseDir, "input");
750  
    File outputDir = new File(ioBaseDir, "output");
751  
    copyInput(originalSrcDir, srcDir);
752  
    copyInput(originalInput, inputDir);
753  
    javax2(srcDir, ioBaseDir, silent, runInProcess, libraries, args, cacheAs, programID, null);
754  
    return outputDir;
755  
  }
756  
757  
  private static void copyInput(File src, File dst) throws IOException {
758  
    copyDirectory(src, dst);
759  
  }
760  
761  
  public static boolean hasFile(File inputDir, String name) {
762  
    return new File(inputDir, name).exists();
763  
  }
764  
765  
  public static void copyDirectory(File src, File dst) throws IOException {
766  
    if (verbose) System.out.println("Copying " + src.getAbsolutePath() + " to " + dst.getAbsolutePath());
767  
    dst.mkdirs();
768  
    File[] files = src.listFiles();
769  
    if (files == null) return;
770  
    for (File file : files) {
771  
      File dst1 = new File(dst, file.getName());
772  
      if (file.isDirectory())
773  
        copyDirectory(file, dst1);
774  
      else {
775  
        if (verbose) System.out.println("Copying " + file.getAbsolutePath() + " to " + dst1.getAbsolutePath());
776  
        copy(file, dst1);
777  
      }
778  
    }
779  
  }
780  
781  
  /** Quickly copy a file without a progress bar or any other fancy GUI... :) */
782  
  public static void copy(File src, File dest) throws IOException {
783  
    FileInputStream inputStream = newFileInputStream(src);
784  
    FileOutputStream outputStream = newFileOutputStream(dest);
785  
    try {
786  
      copy(inputStream, outputStream);
787  
      inputStream.close();
788  
    } finally {
789  
      outputStream.close();
790  
    }
791  
  }
792  
793  
  private static FileInputStream newFileInputStream(File f) throws FileNotFoundException {
794  
    /*if (androidContext != null)
795  
      return (FileInputStream) call(androidContext,
796  
        "openFileInput", f.getPath());
797  
    else*/
798  
    return new // line break for Dumb TinyBrain :)
799  
    FileInputStream(f);
800  
  }
801  
802  
  private static FileOutputStream newFileOutputStream(File f) throws FileNotFoundException {
803  
    /*if (androidContext != null)
804  
      return (FileOutputStream) call(androidContext,
805  
        "openFileOutput", f.getPath(), 0);
806  
    else*/
807  
    return new // line break for Dumb TinyBrain :)
808  
    FileOutputStream(f);
809  
  }
810  
811  
  public static void copy(InputStream in, OutputStream out) throws IOException {
812  
    byte[] buf = new byte[65536];
813  
    while (true) {
814  
      int n = in.read(buf);
815  
      if (n <= 0) return;
816  
      out.write(buf, 0, n);
817  
    }
818  
  }
819  
820  
  /** writes safely (to temp file, then rename) */
821  
  public static void saveBinaryFile(String fileName, byte[] contents) throws IOException {
822  
    File file = new File(fileName);
823  
    File parentFile = file.getParentFile();
824  
    if (parentFile != null)
825  
      parentFile.mkdirs();
826  
    String tempFileName = fileName + "_temp";
827  
    FileOutputStream fileOutputStream = newFileOutputStream(new File(tempFileName));
828  
    fileOutputStream.write(contents);
829  
    fileOutputStream.close();
830  
    if (file.exists() && !file.delete())
831  
      throw new IOException("Can't delete " + fileName);
832  
833  
    if (!new File(tempFileName).renameTo(file))
834  
      throw new IOException("Can't rename " + tempFileName + " to " + fileName);
835  
  }
836  
  public static String loadTextFile(String fileName) {
837  
    try {
838  
      return loadTextFile(fileName, null);
839  
    } catch (IOException e) {
840  
      throw new RuntimeException(e);
841  
    }
842  
  }
843  
  
844  
  public static String loadTextFile(String fileName, String defaultContents) throws IOException {
845  
    if (!new File(fileName).exists())
846  
      return defaultContents;
847  
848  
    FileInputStream fileInputStream = new FileInputStream(fileName);
849  
    InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8");
850  
    return loadTextFile(inputStreamReader);
851  
  }
852  
  
853  
  public static String loadTextFile(File fileName) {
854  
    try {
855  
      return loadTextFile(fileName, null);
856  
    } catch (IOException e) {
857  
      throw new RuntimeException(e);
858  
    }
859  
  }
860  
861  
  public static String loadTextFile(File fileName, String defaultContents) throws IOException {
862  
    try {
863  
      return loadTextFile(fileName.getPath(), defaultContents);
864  
    } catch (IOException e) {
865  
      throw new RuntimeException(e);
866  
    }
867  
  }
868  
  
869  
  public static String loadTextFile(Reader reader) throws IOException {
870  
    StringBuilder builder = new StringBuilder();
871  
    try {
872  
      char[] buffer = new char[1024];
873  
      int n;
874  
      while (-1 != (n = reader.read(buffer)))
875  
        builder.append(buffer, 0, n);
876  
        
877  
    } finally {
878  
      reader.close();
879  
    }
880  
    return builder.toString();
881  
  } // loadTextFile
882  
  
883  
  static File DiskSnippetCache_dir;
884  
885  
  public static void initDiskSnippetCache(File dir) {
886  
    DiskSnippetCache_dir = dir;
887  
    dir.mkdirs();
888  
  }
889  
890  
  // Data files are immutable, use centralized cache
891  
  public static synchronized File DiskSnippetCache_getLibrary(long snippetID) throws IOException {
892  
    File file = new File(getGlobalCache(), "data_" + snippetID + ".jar");
893  
    if (verbose)
894  
      System.out.println("Checking data cache: " + file.getPath());
895  
    return file.exists() ? file : null;
896  
  }
897  
898  
  public static synchronized String DiskSnippetCache_get(long snippetID) throws IOException {
899  
    return loadTextFile(DiskSnippetCache_getFile(snippetID).getPath(), null);
900  
  }
901  
902  
  private static File DiskSnippetCache_getFile(long snippetID) {
903  
    return new File(DiskSnippetCache_dir, "" + snippetID);
904  
  }
905  
906  
  public static synchronized void DiskSnippetCache_put(long snippetID, String snippet) throws IOException {
907  
    saveTextFile(DiskSnippetCache_getFile(snippetID).getPath(), snippet);
908  
  }
909  
910  
  public static synchronized void DiskSnippetCache_putLibrary(long snippetID, byte[] data) throws IOException {
911  
    saveBinaryFile(new File(getGlobalCache(), "data_" + snippetID).getPath() + ".jar", data);
912  
  }
913  
914  
  public static File DiskSnippetCache_getDir() {
915  
    return DiskSnippetCache_dir;
916  
  }
917  
918  
  public static void initSnippetCache() {
919  
    if (DiskSnippetCache_dir == null)
920  
      initDiskSnippetCache(getGlobalCache());
921  
  }
922  
923  
  private static File getGlobalCache() {
924  
    File file = new File(userHome(), ".tinybrain/snippet-cache");
925  
    file.mkdirs();
926  
    return file;
927  
  }
928  
929  
  public static String loadSnippetVerified(String snippetID, String hash) throws IOException {
930  
    String text = loadSnippet(snippetID);
931  
    String realHash = getHash(text.getBytes("UTF-8"));
932  
    if (!realHash.equals(hash)) {
933  
      String msg;
934  
      if (hash.isEmpty())
935  
        msg = "Here's your hash for " + snippetID + ", please put in your program: " + realHash;
936  
      else
937  
        msg = "Hash mismatch for " + snippetID + ": " + realHash + " (new) vs " + hash + " - has tinybrain.de been hacked??";
938  
      throw new RuntimeException(msg);
939  
    }
940  
    return text;
941  
  }
942  
943  
  public static String getHash(byte[] data) {
944  
    return bytesToHex(getFullFingerprint(data));
945  
  }
946  
947  
  public static byte[] getFullFingerprint(byte[] data) {
948  
    try {
949  
      return MessageDigest.getInstance("MD5").digest(data);
950  
    } catch (NoSuchAlgorithmException e) {
951  
      throw new RuntimeException(e);
952  
    }
953  
  }
954  
955  
  public static String bytesToHex(byte[] bytes) {
956  
    return bytesToHex(bytes, 0, bytes.length);
957  
  }
958  
959  
  public static String bytesToHex(byte[] bytes, int ofs, int len) {
960  
    StringBuilder stringBuilder = new StringBuilder(len*2);
961  
    for (int i = 0; i < len; i++) {
962  
      String s = "0" + Integer.toHexString(bytes[ofs+i]);
963  
      stringBuilder.append(s.substring(s.length()-2, s.length()));
964  
    }
965  
    return stringBuilder.toString();
966  
  }
967  
968  
  public static String loadSnippet(String snippetID) throws IOException {
969  
    return loadSnippet(parseSnippetID(snippetID));
970  
  }
971  
972  
  public static long parseSnippetID(String snippetID) {
973  
    return Long.parseLong(shortenSnippetID(snippetID));
974  
  }
975  
976  
  private static String shortenSnippetID(String snippetID) {
977  
    if (snippetID.startsWith("#"))
978  
      snippetID = snippetID.substring(1);
979  
    String httpBlaBla = "http://tinybrain.de/";
980  
    if (snippetID.startsWith(httpBlaBla))
981  
      snippetID = snippetID.substring(httpBlaBla.length());
982  
    return snippetID;
983  
  }
984  
985  
  public static boolean isSnippetID(String snippetID) {
986  
    snippetID = shortenSnippetID(snippetID);
987  
    return isInteger(snippetID) && Long.parseLong(snippetID) != 0;
988  
  }
989  
990  
  public static boolean isInteger(String s) {
991  
    return Pattern.matches("\\-?\\d+", s);
992  
  }
993  
  
994  
  static String getTranspilationFromBossBot(long snippetID) {
995  
    return boss(format3("get transpilation for *", snippetID));
996  
  }
997  
  
998  
  public static String loadSnippet(long snippetID) throws IOException {
999  
    String text = getSnippetFromBossBot(snippetID);
1000  
    if (text != null) return text;
1001  
    
1002  
    text = memSnippetCache.get(snippetID);
1003  
    if (text != null) {
1004  
      if (verbose)
1005  
        System.out.println("Getting " + snippetID + " from mem cache");
1006  
      return text;
1007  
    }
1008  
    
1009  
    initSnippetCache();
1010  
    text = DiskSnippetCache_get(snippetID);
1011  
    if (preferCached && text != null) {
1012  
      if (verbose)
1013  
        System.out.println("Getting " + snippetID + " from disk cache (preferCached)");
1014  
      return text;
1015  
    }
1016  
1017  
    String md5 = text != null ? md5(text) : "-";
1018  
    if (text != null) {
1019  
      String hash = prefetched.get(snippetID);
1020  
      if (hash != null) {
1021  
        if (md5.equals(hash)) {
1022  
          memSnippetCache.put(snippetID, text);
1023  
          if (verbose)
1024  
            System.out.println("Getting " + snippetID + " from prefetched");
1025  
          return text;
1026  
        } else
1027  
          prefetched.remove(snippetID); // (maybe this is not necessary)
1028  
      }
1029  
    }
1030  
1031  
    try {
1032  
      /*URL url = new URL(tb_mainServer() + "/getraw.php?id=" + snippetID + standardCredentials());
1033  
      text = loadPage(url);*/
1034  
      String theURL = tb_mainServer() + "/getraw.php?id=" + snippetID + "&getmd5=1&utf8=1&usetranspiled=1" + standardCredentials();
1035  
      if (text != null) {
1036  
        //System.err.println("MD5: " + md5);
1037  
        theURL += "&md5=" + md5;
1038  
      }
1039  
      URL url = new URL(theURL);
1040  
      String page = loadPage(url);
1041  
1042  
      // parse & drop transpilation flag available line
1043  
      int i = page.indexOf('\n');
1044  
      boolean hasTranspiled = page.substring(0, i).trim().equals("1");
1045  
      if (hasTranspiled)
1046  
        hasTranspiledSet.add(snippetID);
1047  
      else
1048  
        hasTranspiledSet.remove(snippetID);
1049  
      page = page.substring(i+1);
1050  
1051  
      if (page.startsWith("==*#*==")) {
1052  
        // same, keep text
1053  
        //System.err.println("Snippet unchanged, keeping.");
1054  
      } else {
1055  
        // drop md5 line
1056  
        i = page.indexOf('\n');
1057  
        String hash = page.substring(0, i).trim();
1058  
        text = page.substring(i+1);
1059  
1060  
        String myHash = md5(text);
1061  
        if (myHash.equals(hash)) {
1062  
          //System.err.println("Hash match: " + hash);
1063  
        } else
1064  
          System.err.println("Hash mismatch");
1065  
      }
1066  
    } catch (Exception e) {
1067  
      e.printStackTrace();
1068  
      throw new IOException("Snippet #" + snippetID + " not found or not public / " + e);
1069  
    }
1070  
1071  
    memSnippetCache.put(snippetID, text);
1072  
1073  
    try {
1074  
      initSnippetCache();
1075  
      DiskSnippetCache_put(snippetID, text);
1076  
    } catch (IOException e) {
1077  
      System.err.println("Minor warning: Couldn't save snippet to cache ("  + DiskSnippetCache_getDir() + ")");
1078  
    }
1079  
1080  
    return text;
1081  
  }
1082  
1083  
  private static String md5(String text) {
1084  
    try {
1085  
      return bytesToHex(md5impl(text.getBytes("UTF-8"))); // maybe different than the way PHP does it...
1086  
    } catch (UnsupportedEncodingException e) {
1087  
      throw new RuntimeException(e);
1088  
    }
1089  
  }
1090  
1091  
  public static byte[] md5impl(byte[] data) {
1092  
    try {
1093  
      return MessageDigest.getInstance("MD5").digest(data);
1094  
    } catch (NoSuchAlgorithmException e) {
1095  
      throw new RuntimeException(e);
1096  
    }
1097  
  }
1098  
1099  
  public static byte[] loadBinaryPage(URLConnection con) throws IOException {
1100  
    setHeaders(con);
1101  
    return loadBinaryPage_noHeaders(con);
1102  
  }
1103  
1104  
  private static byte[] loadBinaryPage_noHeaders(URLConnection con) throws IOException {
1105  
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
1106  
    InputStream inputStream = con.getInputStream();
1107  
    int n = 0;
1108  
    while (true) {
1109  
      int ch = inputStream.read();
1110  
      if (ch < 0)
1111  
        break;
1112  
      buf.write(ch);
1113  
      if (++n % 100000 == 0)
1114  
        System.err.println("  " + n + " bytes loaded.");
1115  
    }
1116  
    inputStream.close();
1117  
    return buf.toByteArray();
1118  
  }
1119  
1120  
  private static void setHeaders(URLConnection con) throws IOException {
1121  
    String computerID = getComputerID();
1122  
    if (computerID != null) try {
1123  
      con.setRequestProperty("X-ComputerID", computerID);
1124  
      con.setRequestProperty("X-OS", System.getProperty("os.name") + " " + System.getProperty("os.version"));
1125  
    } catch (Throwable e) {}
1126  
  }
1127  
1128  
  public static String guessCharset(String contentType) {
1129  
    Pattern p = Pattern.compile("text/html;\\s+charset=([^\\s]+)\\s*");
1130  
    Matcher m = p.matcher(contentType);
1131  
    /* If Content-Type doesn't match this pre-conception, choose default and hope for the best. */
1132  
    return m.matches() ? m.group(1) : "ISO-8859-1";
1133  
  }
1134  
1135  
  /** runs a transpiled set of sources */
1136  
  public static Class javax2(File srcDir, File ioBaseDir, boolean silent, boolean runInProcess,
1137  
                            List<File> libraries, String[] args, String cacheAs,
1138  
                            String programID, Info info) throws Exception {
1139  
    return javax2android(srcDir, args, programID);
1140  
  }
1141  
1142  
  static Class<?> loadx2android(File srcDir, String programID) throws Exception {
1143  
    File dexDir = TempDirMaker_make();
1144  
    File dexFile = new File(dexDir, System.currentTimeMillis() + ".dex");
1145  
    byte[] dexData;
1146  
    
1147  
    if (isSnippetID(programID))
1148  
      dexData = loadBinaryPage(tb_mainServer() + "/dexcompile.php?id=" + parseSnippetID(programID) + standardCredentials());
1149  
    else {
1150  
      URL url = new URL(tb_mainServer() + "/dexcompile.php");
1151  
      URLConnection conn = url.openConnection();
1152  
      String postData = "src=" + URLEncoder.encode(loadTextFile(new File(srcDir, "main.java").getPath(), null), "UTF-8");
1153  
      dexData = doPostBinary(postData, conn);
1154  
    }
1155  
    
1156  
    if (!isDex(dexData))
1157  
      throw new RuntimeException("Dex generation error: " + dexData.length + " bytes - " + new String(dexData, "UTF-8"));
1158  
    //System.out.println("Dex loaded: " + dexData.length + "b");
1159  
1160  
    File dexOutputDir = TempDirMaker_makeInternal();
1161  
1162  
    //System.out.println("Saving dex to: " + dexDir.getAbsolutePath());
1163  
    try {
1164  
      saveBinaryFile(dexFile.getPath(), dexData);
1165  
    } catch (Throwable e) {
1166  
      System.out.println("Whoa!");
1167  
      throw new RuntimeException(e);
1168  
    }
1169  
1170  
    //System.out.println("Getting parent class loader.");
1171  
    ClassLoader parentClassLoader =
1172  
      //ClassLoader.getSystemClassLoader(); // does not find support jar
1173  
      //getClass().getClassLoader(); // Let's try this...
1174  
      x30.class.getClassLoader().getParent(); // XXX !
1175  
1176  
    //System.out.println("Making DexClassLoader.");
1177  
    //DexClassLoader classLoader = new DexClassLoader(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null,
1178  
    //  parentClassLoader);
1179  
    Class dcl = Class.forName("dalvik.system.DexClassLoader");
1180  
    Object classLoader = dcl.getConstructors()[0].newInstance(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null,
1181  
      parentClassLoader);
1182  
1183  
    //System.out.println("Loading main class.");
1184  
    //Class<?> theClass = classLoader.loadClass(mainClassName);
1185  
    Class<?> theClass = (Class<?>) call(classLoader, "loadClass", "main");
1186  
1187  
    //System.out.println("Main class loaded.");
1188  
    try {
1189  
      set(theClass, "androidContext", androidContext);
1190  
    } catch (Throwable e) {}
1191  
1192  
    setVars(theClass, programID);
1193  
    
1194  
    addInstance(programID, theClass);
1195  
    
1196  
    return theClass;
1197  
  }
1198  
  
1199  
  static void addInstance(String programID, Class mainClass) {
1200  
    programID = "" + parseSnippetID(programID);
1201  
    instances.put(programID, new WeakReference<Class>(mainClass));
1202  
  }
1203  
  
1204  
  static Class getInstance(String programID) {
1205  
    programID = "" + parseSnippetID(programID);
1206  
    List<WeakReference<Class>> l = instances.get(programID);
1207  
    for (WeakReference<Class> c : l) {
1208  
      Class theClass = c.get();
1209  
      // TODO: shorten the list
1210  
      if (theClass != null)
1211  
        return theClass;
1212  
    }
1213  
    return null;
1214  
  }
1215  
  
1216  
  static MultiMap<String, WeakReference<Class>> instances = new MultiMap<String, WeakReference<Class>>();
1217  
static class MultiMap<A,B> {
1218  
  Map<A, List<B>> data = new HashMap<A, List<B>>();
1219  
  
1220  
  MultiMap() {}
1221  
  MultiMap(MultiMap<A, B> map) { putAll(map); }
1222  
1223  
  public void put(A key, B value) {
1224  
    List<B> list = data.get(key);
1225  
    if (list == null)
1226  
      data.put(key, list = new ArrayList<B>());
1227  
    list.add(value);
1228  
  }
1229  
1230  
  public void addAll(A key, Collection<B> values) {
1231  
    putAll(key, values);
1232  
  }
1233  
  
1234  
  public void addAllIfNotThere(A key, Collection<B> values) {
1235  
    for (B value : values)
1236  
      setPut(key, value);
1237  
  }
1238  
  
1239  
  void setPut(A key, B value) {
1240  
    if (!containsPair(key, value))
1241  
      put(key, value);
1242  
  }
1243  
  
1244  
  boolean containsPair(A key, B value) {
1245  
    return get(key).contains(value);
1246  
  }
1247  
  
1248  
  public void putAll(A key, Collection<B> values) {
1249  
    for (B value : values)
1250  
      put(key, value);
1251  
  }
1252  
1253  
  void removeAll(A key, Collection<B> values) {
1254  
    for (B value : values)
1255  
      remove(key, value);
1256  
  }
1257  
  
1258  
  public List<B> get(A key) {
1259  
    List<B> list = data.get(key);
1260  
    return list == null ? Collections.<B> emptyList() : list;
1261  
  }
1262  
1263  
  // returns actual mutable live list
1264  
  // creates the list if not there
1265  
  public List<B> getActual(A key) {
1266  
    List<B> list = data.get(key);
1267  
    if (list == null)
1268  
      data.put(key, list = litlist());
1269  
    return list;
1270  
  }
1271  
 
1272  
  void clean(A key) {
1273  
    List<B> list = data.get(key);
1274  
    if (list != null && list.isEmpty())
1275  
      data.remove(key);
1276  
  }
1277  
1278  
  public Set<A> keySet() {
1279  
    return data.keySet();
1280  
  }
1281  
1282  
  public Set<A> keys() {
1283  
    return data.keySet();
1284  
  }
1285  
1286  
  public void remove(A key) {
1287  
    data.remove(key);
1288  
  }
1289  
1290  
  public void remove(A key, B value) {
1291  
    List<B> list = data.get(key);
1292  
    if (list != null) {
1293  
      list.remove(value);
1294  
      if (list.isEmpty())
1295  
        data.remove(key);
1296  
    }
1297  
  }
1298  
1299  
  public void clear() {
1300  
    data.clear();
1301  
  }
1302  
1303  
  public boolean containsKey(A key) {
1304  
    return data.containsKey(key);
1305  
  }
1306  
1307  
  public B getFirst(A key) {
1308  
    List<B> list = get(key);
1309  
    return list.isEmpty() ? null : list.get(0);
1310  
  }
1311  
  
1312  
  public void putAll(MultiMap<A, B> map) {
1313  
    for (A key : map.keySet())
1314  
      putAll(key, map.get(key));
1315  
  }
1316  
  
1317  
  // note: expensive operation
1318  
  int size() {
1319  
    int n = 0;
1320  
    for (List l : data.values())
1321  
      n += l(l);
1322  
    return n;
1323  
  }
1324  
  
1325  
  // expensive operation
1326  
  List<A> reverseGet(B b) {
1327  
    List<A> l = new ArrayList<A>();
1328  
    for (A key : data.keySet())
1329  
      if (data.get(key).contains(b))
1330  
        l.add(key);
1331  
    return l;
1332  
  }
1333  
} // MultiMap
1334  
1335  
  static Class<?> javax2android(File srcDir, String[] args, String programID) throws Exception {
1336  
    Class<?> theClass = loadx2android(srcDir, programID);
1337  
1338  
    // record injection
1339  
    if (l(args) == 1 && empty(first(args))) args = new String[0];
1340  
    final PaA paa = new PaA(programID, args);
1341  
    paa.injectionID = randomID(8);
1342  
    paa.mainClass = theClass;
1343  
    addInjection(paa);
1344  
    callF(getAndClearThreadLocal(onProgramLoad), paa);
1345  
    
1346  
    Method main = null;
1347  
    try {
1348  
      main = call_findStaticMethod(theClass, "main", new Object[] { androidContext }, false);
1349  
    } catch (RuntimeException e) {
1350  
    }
1351  
1352  
    //System.out.println("main method for " + androidContext + " of " + theClass + ": " + main);
1353  
1354  
    if (main != null) {
1355  
      // old style main program that returns a View
1356  
      // TODO: maybe allow programs without main method, although it doesn't seem to make sense here really (Android main program)
1357  
      System.out.println("Calling main (old-style)");
1358  
      Object view = main.invoke(null, androidContext);
1359  
      System.out.println("Calling setContentView with " + view);
1360  
      call(Class.forName("main"), "setContentViewInUIThread", view);
1361  
      //call(androidContext, "setContentView", view);
1362  
      System.out.println("Done.");
1363  
    } else {
1364  
      //System.out.println("New-style main method running.\n\n====\n");
1365  
      runMainMethod(args, theClass);
1366  
    }
1367  
    
1368  
    return theClass;
1369  
  }
1370  
1371  
  static byte[] DEX_FILE_MAGIC = { 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 };
1372  
1373  
  static boolean isDex(byte[] dexData) {
1374  
    if (dexData.length < DEX_FILE_MAGIC.length) return false;
1375  
    for (int i = 0; i < DEX_FILE_MAGIC.length; i++)
1376  
      if (dexData[i] != DEX_FILE_MAGIC[i])
1377  
        return false;
1378  
    return true;
1379  
  }
1380  
1381  
  static byte[] doPostBinary(String urlParameters, URLConnection conn) throws IOException {
1382  
    // connect and do POST
1383  
    setHeaders(conn);
1384  
    conn.setDoOutput(true);
1385  
1386  
    OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
1387  
    writer.write(urlParameters);
1388  
    writer.flush();
1389  
1390  
    byte[] contents = loadBinaryPage_noHeaders(conn);
1391  
    writer.close();
1392  
    return contents;
1393  
  }
1394  
1395  
  static String compileJava(File srcDir, List<File> libraries, File classesDir) throws IOException {
1396  
    javaCompilerOutput = null;
1397  
    ++compilations;
1398  
1399  
    // collect sources
1400  
1401  
    List<File> sources = new ArrayList<File>();
1402  
    if (verbose) System.out.println("Scanning for sources in " + srcDir.getPath());
1403  
    scanForSources(srcDir, sources, true);
1404  
    if (sources.isEmpty())
1405  
      throw new IOException("No sources found");
1406  
1407  
    // compile
1408  
1409  
    File optionsFile = File.createTempFile("javax", "");
1410  
    if (verbose) System.out.println("Compiling " + sources.size() + " source(s) to " + classesDir.getPath());
1411  
    if (verbose) System.out.println("Libraries: " + libraries);
1412  
    String options = "-d " + bashQuote(classesDir.getPath());
1413  
    writeOptions(sources, libraries, optionsFile, options);
1414  
    classesDir.mkdirs();
1415  
    return invokeJavaCompiler(optionsFile);
1416  
  }
1417  
1418  
  private static void runProgram(String javacOutput, File classesDir, File ioBaseDir,
1419  
                                 boolean silent, boolean runInProcess,
1420  
                                 List<File> libraries, String[] args, String cacheAs,
1421  
                                 String programID, Info info) throws Exception {
1422  
    // print javac output if compile failed and it hasn't been printed yet
1423  
    if (info != null) {
1424  
      info.programID = programID;
1425  
      info.programArgs = args;
1426  
    }
1427  
    boolean didNotCompile = !didCompile(classesDir);
1428  
    if (verbose || didNotCompile)
1429  
      System.out.println(javacOutput);
1430  
    if (didNotCompile)
1431  
      return;
1432  
1433  
    if (runInProcess
1434  
      || (ioBaseDir.getAbsolutePath().equals(new File(".").getAbsolutePath()) && !silent)) {
1435  
      runProgramQuick(classesDir, libraries, args, cacheAs, programID, info, ioBaseDir);
1436  
      return;
1437  
    }
1438  
1439  
    boolean echoOK = false;
1440  
    // TODO: add libraries to class path
1441  
    String bashCmd = "(cd " + bashQuote(ioBaseDir.getAbsolutePath()) + " && (java -cp "
1442  
      + bashQuote(classesDir.getAbsolutePath()) + " main" + (echoOK ? "; echo ok" : "") + "))";
1443  
    if (verbose) System.out.println(bashCmd);
1444  
    String output = backtick(bashCmd);
1445  
    lastOutput = output;
1446  
    if (verbose || !silent)
1447  
      System.out.println(output);
1448  
  }
1449  
1450  
  static boolean didCompile(File classesDir) {
1451  
    return hasFile(classesDir, "main.class");
1452  
  }
1453  
1454  
  private static void runProgramQuick(File classesDir, List<File> libraries,
1455  
                                      String[] args, String cacheAs,
1456  
                                      String programID, Info info,
1457  
                                      File ioBaseDir) throws Exception {
1458  
    // collect urls
1459  
    URL[] urls = new URL[libraries.size()+1];
1460  
    urls[0] = classesDir.toURI().toURL();
1461  
    for (int i = 0; i < libraries.size(); i++)
1462  
      urls[i+1] = libraries.get(i).toURI().toURL();
1463  
1464  
    // make class loader
1465  
    URLClassLoader classLoader = new URLClassLoader(urls);
1466  
1467  
    // load JavaX main class
1468  
    Class<?> mainClass = classLoader.loadClass("main");
1469  
    
1470  
    if (info != null) {
1471  
      info.mainClass = mainClass;
1472  
      if (info.transpiledSrc != null)
1473  
        registerSourceCode(mainClass, loadTextFile(new File(info.transpiledSrc, "main.java")));
1474  
    }
1475  
      
1476  
    if (cacheAs != null)
1477  
      programCache.put(cacheAs, mainClass);
1478  
      
1479  
    // record injection
1480  
    final PaA paa = new PaA(programID, args);
1481  
    paa.injectionID = randomID(8);
1482  
    paa.mainClass = mainClass;
1483  
    addInjection(paa);
1484  
1485  
    // change baseDir
1486  
    try {
1487  
      //print("Changing base dir to " + ioBaseDir.getAbsolutePath());
1488  
      Class virtual = mainClass.getClassLoader().loadClass("virtual");
1489  
      set(virtual, "virtual_baseDir", ioBaseDir.getAbsolutePath());
1490  
    } catch (Throwable e) { /* whatever */ }
1491  
1492  
    setVars(mainClass, programID);
1493  
    addInstance(programID, mainClass);
1494  
    try {
1495  
      runMainMethod(args, mainClass);
1496  
    } catch (Exception e) {
1497  
      paa.exception = e;
1498  
      throw e;
1499  
    } finally {
1500  
      paa.mainDone = true;
1501  
    }
1502  
  }
1503  
1504  
  static void setVars(Class<?> theClass, String programID) {
1505  
    try {
1506  
      set(theClass, "programID", programID);
1507  
    } catch (Throwable e) {}
1508  
1509  
    try {
1510  
      set(theClass, "__javax", x30.class);
1511  
    } catch (Throwable e) {}
1512  
  }
1513  
1514  
1515  
  static void runMainMethod(String[] args, Class<?> mainClass) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
1516  
    callMain(mainClass, args);
1517  
  }
1518  
1519  
  static String invokeJavaCompiler(File optionsFile) throws IOException {
1520  
    String output;
1521  
    if (hasEcj() && !javacOnly)
1522  
      output = invokeEcj(optionsFile);
1523  
    else
1524  
      output = invokeJavac(optionsFile);
1525  
    if (verbose) System.out.println(output);
1526  
    return output;
1527  
  }
1528  
1529  
  private static boolean hasEcj() {
1530  
    try {
1531  
      Class.forName("org.eclipse.jdt.internal.compiler.batch.Main");
1532  
      return true;
1533  
    } catch (ClassNotFoundException e) {
1534  
      return false;
1535  
    }
1536  
  }
1537  
1538  
  // TODO: fix UTF-8 here too
1539  
  private static String invokeJavac(File optionsFile) throws IOException {
1540  
    String output;
1541  
    output = backtick("javac " + bashQuote("@" + optionsFile.getPath()));
1542  
    javaCompilerOutput = output;
1543  
    if (exitValue != 0) {
1544  
      System.out.println(output);
1545  
      throw new RuntimeException("javac returned errors.");
1546  
    }
1547  
    return output;
1548  
  }
1549  
1550  
  // throws ClassNotFoundException if ecj is not in classpath
1551  
  static String invokeEcj(File optionsFile) {
1552  
    try {
1553  
      StringWriter writer = new StringWriter();
1554  
      PrintWriter printWriter = new PrintWriter(writer);
1555  
1556  
      // add more eclipse options in the line below
1557  
1558  
      String[] args = {
1559  
        "-source", javaTarget,
1560  
        "-target", javaTarget,
1561  
        "-nowarn",
1562  
        "-encoding", "UTF-8",
1563  
        "@" + optionsFile.getPath()
1564  
      };
1565  
      
1566  
      if (verbose)
1567  
        print("ECJ options: " + structure(args));
1568  
1569  
      Class ecjClass = Class.forName("org.eclipse.jdt.internal.compiler.batch.Main");
1570  
      Object main = newInstance(ecjClass, printWriter, printWriter, false);
1571  
      call(main, "compile", new Object[]{args});
1572  
      int errors = (Integer) get(main, "globalErrorsCount");
1573  
1574  
      String output = writer.toString();
1575  
      javaCompilerOutput = output;
1576  
      if (errors != 0) {
1577  
        System.out.println(output);
1578  
        throw new RuntimeException("Java compiler returned errors.");
1579  
      }
1580  
      return output;
1581  
    } catch (Exception e) {
1582  
      throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
1583  
    }
1584  
  }
1585  
1586  
  static Object newInstance(Class c, Object... args) { try {
1587  
    Constructor m = findConstructor(c, args);
1588  
    m.setAccessible(true);
1589  
    return m.newInstance(args);
1590  
  } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
1591  
1592  
  static Constructor findConstructor(Class c, Object... args) {
1593  
    for (Constructor m : c.getDeclaredConstructors()) {
1594  
      if (!checkArgs(m.getParameterTypes(), args, verbose))
1595  
        continue;
1596  
      return m;
1597  
    }
1598  
    throw new RuntimeException("Constructor with " + args.length + " matching parameter(s) not found in " + c.getName());
1599  
  }
1600  
1601  
  static boolean checkArgs(Class[] types, Object[] args, boolean debug) {
1602  
    if (types.length != args.length) {
1603  
      if (debug)
1604  
        System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
1605  
      return false;
1606  
    }
1607  
    for (int i = 0; i < types.length; i++)
1608  
      if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
1609  
        if (debug)
1610  
          System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
1611  
        return false;
1612  
      }
1613  
    return true;
1614  
  }
1615  
1616  
  private static void writeOptions(List<File> sources, List<File> libraries,
1617  
                                   File optionsFile, String moreOptions) throws IOException {
1618  
    FileWriter writer = new FileWriter(optionsFile);
1619  
    for (File source : sources)
1620  
      writer.write(bashQuote(source.getPath()) + " ");
1621  
    if (!libraries.isEmpty()) {
1622  
      List<String> cp = new ArrayList<String>();
1623  
      for (File lib : libraries)
1624  
        cp.add(lib.getAbsolutePath());
1625  
      writer.write("-cp " + bashQuote(join(File.pathSeparator, cp)) + " ");
1626  
    }
1627  
    writer.write(moreOptions);
1628  
    writer.close();
1629  
  }
1630  
1631  
  static void scanForSources(File source, List<File> sources, boolean topLevel) {
1632  
    if (source.isFile() && source.getName().endsWith(".java"))
1633  
      sources.add(source);
1634  
    else if (source.isDirectory() && !isSkippedDirectoryName(source.getName(), topLevel)) {
1635  
      File[] files = source.listFiles();
1636  
      for (File file : files)
1637  
        scanForSources(file, sources, false);
1638  
    }
1639  
  }
1640  
1641  
  private static boolean isSkippedDirectoryName(String name, boolean topLevel) {
1642  
    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.)
1643  
    return name.equalsIgnoreCase("input") || name.equalsIgnoreCase("output");
1644  
  }
1645  
1646  
  static int exitValue;
1647  
  public static String backtick(String cmd) throws IOException {
1648  
    ++processesStarted;
1649  
    File outFile = File.createTempFile("_backtick", "");
1650  
    File scriptFile = File.createTempFile("_backtick", isWindows() ? ".bat" : "");
1651  
1652  
    String command = cmd + " >" + bashQuote(outFile.getPath()) + " 2>&1";
1653  
    //Log.info("[Backtick] " + command);
1654  
    try {
1655  
      saveTextFile(scriptFile.getPath(), command);
1656  
      String[] command2;
1657  
      if (isWindows())
1658  
        command2 = new String[] { scriptFile.getPath() };
1659  
      else
1660  
        command2 = new String[] { "/bin/bash", scriptFile.getPath() };
1661  
      Process process = Runtime.getRuntime().exec(command2);
1662  
      try {
1663  
        process.waitFor();
1664  
      } catch (InterruptedException e) {
1665  
        throw new RuntimeException(e);
1666  
      }
1667  
      exitValue = process.exitValue();
1668  
      if (verbose)
1669  
        System.out.println("Process return code: " + exitValue);
1670  
      return loadTextFile(outFile.getPath(), "");
1671  
    } finally {
1672  
      scriptFile.delete();
1673  
    }
1674  
  }
1675  
1676  
  /** possibly improvable */
1677  
  public static String javaQuote(String text) {
1678  
    return bashQuote(text);
1679  
  }
1680  
1681  
  /** possibly improvable */
1682  
  public static String bashQuote(String text) {
1683  
    if (text == null) return null;
1684  
    return "\"" + text
1685  
      .replace("\\", "\\\\")
1686  
      .replace("\"", "\\\"")
1687  
      .replace("\n", "\\n")
1688  
      .replace("\r", "\\r") + "\"";
1689  
  }
1690  
1691  
  public final static String charsetForTextFiles = "UTF8";
1692  
1693  
  static long TempDirMaker_lastValue;
1694  
1695  
  public static File TempDirMaker_makeInternal() {
1696  
    File dir = new File(userHomeInternal(), ".javax/" + TempDirMaker_newValue());
1697  
    dir.mkdirs();
1698  
    return dir;
1699  
  }
1700  
1701  
  public static File TempDirMaker_make() {
1702  
    File dir = new File(userHome(), ".javax/" + TempDirMaker_newValue());
1703  
    dir.mkdirs();
1704  
    return dir;
1705  
  }
1706  
1707  
  private static long TempDirMaker_newValue() {
1708  
    long value;
1709  
    do
1710  
      value = System.currentTimeMillis();
1711  
    while (value == TempDirMaker_lastValue);
1712  
    TempDirMaker_lastValue = value;
1713  
    return value;
1714  
  }
1715  
  public static String join(String glue, Iterable<String> strings) {
1716  
    StringBuilder buf = new StringBuilder();
1717  
    Iterator<String> i = strings.iterator();
1718  
    if (i.hasNext()) {
1719  
      buf.append(i.next());
1720  
      while (i.hasNext())
1721  
        buf.append(glue).append(i.next());
1722  
    }
1723  
    return buf.toString();
1724  
  }
1725  
  
1726  
  public static String join(String glue, String[] strings) {
1727  
    return join(glue, Arrays.asList(strings));
1728  
  }
1729  
  
1730  
  public static String join(Iterable<String> strings) {
1731  
    return join("", strings);
1732  
  }
1733  
  
1734  
  public static String join(String[] strings) {
1735  
    return join("", strings);
1736  
  }
1737  
 // join
1738  
  
1739  
  public static boolean isWindows() {
1740  
    return System.getProperty("os.name").contains("Windows");
1741  
  }
1742  
1743  
  public static String makeRandomID(int length) {
1744  
    Random random = new Random();
1745  
    char[] id = new char[length];
1746  
    for (int i = 0; i< id.length; i++)
1747  
      id[i] = (char) ((int) 'a' + random.nextInt(26));
1748  
    return new String(id);
1749  
  }
1750  
1751  
  static String computerID;
1752  
  public static String getComputerID() throws IOException {
1753  
    if (noID) return null;
1754  
    if (computerID == null) {
1755  
      File file = new File(userHome(), ".tinybrain/computer-id");
1756  
      computerID = loadTextFile(file.getPath(), null);
1757  
      if (computerID == null) {
1758  
        computerID = makeRandomID(12);
1759  
        saveTextFile(file.getPath(), computerID);
1760  
      }
1761  
      if (verbose)
1762  
        System.out.println("Local computer ID: " + computerID);
1763  
    }
1764  
    return computerID;
1765  
  }
1766  
1767  
  static void cleanCache() {
1768  
    cleanJavaXCache(tempFileRetentionTime, verbose);
1769  
  }
1770  
1771  
  static void showSystemProperties() {
1772  
    System.out.println("System properties:\n");
1773  
    for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
1774  
      System.out.println("  " + entry.getKey() + " = " + entry.getValue());
1775  
    }
1776  
    System.out.println();
1777  
  }
1778  
1779  
  static void showVersion() {
1780  
    //showSystemProperties();
1781  
    boolean eclipseFound = hasEcj();
1782  
    //String platform = System.getProperty("java.vendor") + " " + System.getProperty("java.runtime.name") + " " + System.getProperty("java.version");
1783  
    String platform = System.getProperty("java.vm.name") + " " + System.getProperty("java.version");
1784  
    String os = System.getProperty("os.name"), arch = System.getProperty("os.arch");
1785  
    System.out.println("This is " + version + ".");
1786  
    System.out.println("[Details: " +
1787  
      (eclipseFound ? "Eclipse compiler (good)" : "javac (not so good)")
1788  
      + ", " + platform + ", " + arch + ", " + os + "]");
1789  
  }
1790  
1791  
  static boolean isAndroid() {
1792  
    return System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0;
1793  
  }
1794  
  static void set(Object o, String field, Object value) {
1795  
    if (o instanceof Class) set((Class) o, field, value);
1796  
    else try {
1797  
      Field f = set_findField(o.getClass(), field);
1798  
      smartSet(f, o, value);
1799  
    } catch (Exception e) {
1800  
      throw new RuntimeException(e);
1801  
    }
1802  
  }
1803  
  
1804  
  static void set(Class c, String field, Object value) {
1805  
    try {
1806  
      Field f = set_findStaticField(c, field);
1807  
      smartSet(f, null, value);
1808  
    } catch (Exception e) {
1809  
      throw new RuntimeException(e);
1810  
    }
1811  
  }
1812  
  
1813  
  static Field set_findField(Class<?> c, String field) {
1814  
    for (Field f : c.getDeclaredFields())
1815  
      if (f.getName().equals(field))
1816  
        return f;
1817  
    throw new RuntimeException("Field '" + field + "' not found in " + c.getName());
1818  
  }
1819  
  
1820  
  static Field set_findStaticField(Class<?> c, String field) {
1821  
    for (Field f : c.getDeclaredFields())
1822  
      if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0)
1823  
        return f;
1824  
    throw new RuntimeException("Static field '" + field + "' not found in " + c.getName());
1825  
  } // set function
1826  
1827  
  static String smartJoin(String[] args) {
1828  
    String[] a2 = new String[args.length];
1829  
    for (int i = 0; i < args.length; i++) {
1830  
      a2[i] = Pattern.compile("\\w+").matcher(args[i]).matches() ? args[i] : quote(args[i]);
1831  
    }
1832  
    return join(" ", a2);
1833  
  }
1834  
  
1835  
  static void logStart(String[] args) throws IOException {
1836  
    String line = smartJoin(args);
1837  
    appendToLog(new File(userHome(), ".javax/log.txt").getPath(), line);
1838  
  }
1839  
  
1840  
  static String quote(String s) {
1841  
    if (s == null) return "null";
1842  
    return "\"" + s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\r", "\\r").replace("\n", "\\n") + "\"";
1843  
  }
1844  
  
1845  
  static void appendToLog(String path, String line) throws IOException {
1846  
    appendToFile(path, "\n" + line + "\n");
1847  
  }
1848  
  
1849  
  static void appendToFile(String path, String s) throws IOException {
1850  
    new File(path).getParentFile().mkdirs();
1851  
    Writer writer = new BufferedWriter(new OutputStreamWriter(
1852  
      new FileOutputStream(path, true), "UTF-8"));
1853  
    writer.write(s);
1854  
    writer.close();
1855  
  }
1856  
  
1857  
  //// END CONSOLE STUFF
1858  
1859  
  static long now_virtualTime;
1860  
  static long now() {
1861  
    return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis();
1862  
  }
1863  
1864  
  static void print(Object o) {
1865  
    System.out.println(o);
1866  
  }
1867  
1868  
  public synchronized void run()
1869  
  {
1870  
  }
1871  
1872  
  static void nohupJavax(String javaxargs) {
1873  
    try {
1874  
      File xfile = new File(userHome(), ".javax/x30.jar");
1875  
      if (!xfile.isFile()) {
1876  
        String url = "http://tinybrain.de/x30.jar";
1877  
        byte[] data = loadBinaryPage(new URL(url).openConnection());
1878  
        if (data.length < 1000000)
1879  
          throw new RuntimeException("Could not load " + url);
1880  
        saveBinaryFile(xfile.getPath(), data);
1881  
      }
1882  
      String jarPath = xfile.getPath();
1883  
      nohup("java -jar " + (isWindows() ? winQuote(jarPath) : bashQuote(jarPath)) + " " + javaxargs);
1884  
    } catch (Exception e) { throw new RuntimeException(e); }
1885  
  }
1886  
1887  
  /** possibly improvable */
1888  
  public static String winQuote(String text) {
1889  
    if (text == null) return null;
1890  
    return "\"" + text
1891  
      .replace("\\", "\\\\")
1892  
      .replace("\"", "\\\"")
1893  
      .replace("\n", "\\n")
1894  
      .replace("\r", "\\r") + "\"";
1895  
  }
1896  
1897  
  public static File nohup(String cmd) throws IOException {
1898  
    File outFile = File.createTempFile("nohup_" + nohup_sanitize(cmd), ".out");
1899  
    nohup(cmd, outFile, false);
1900  
    return outFile;
1901  
  }
1902  
1903  
  static String nohup_sanitize(String s) {
1904  
    return s.replaceAll("[^a-zA-Z0-9\\-_]", "");
1905  
  }
1906  
1907  
  /** outFile takes stdout and stderr. */
1908  
  public static void nohup(String cmd, File outFile, boolean append) throws IOException {
1909  
    String command = nohup_makeNohupCommand(cmd, outFile, append);
1910  
1911  
    File scriptFile = File.createTempFile("_realnohup", isWindows() ? ".bat" : "");
1912  
    System.out.println("[Nohup] " + command);
1913  
    try {
1914  
      //System.out.println("[RealNohup] Script file: " + scriptFile.getPath());
1915  
      saveTextFile(scriptFile.getPath(), command);
1916  
      String[] command2;
1917  
      if (isWindows())
1918  
        command2 = new String[] {"cmd", "/c", "start", "/b", scriptFile.getPath() };
1919  
      else
1920  
        command2 = new String[] {"/bin/bash", scriptFile.getPath() };
1921  
1922  
      Process process = Runtime.getRuntime().exec(command2);
1923  
      try {
1924  
        process.waitFor();
1925  
      } catch (InterruptedException e) {
1926  
        throw new RuntimeException(e);
1927  
      }
1928  
      int value = process.exitValue();
1929  
      //System.out.println("exit value: " + value);
1930  
    } finally {
1931  
      if (!isWindows())
1932  
        scriptFile.delete();
1933  
    }
1934  
  }
1935  
1936  
  public static String nohup_makeNohupCommand(String cmd, File outFile, boolean append) {
1937  
    mkdirsForFile(outFile);
1938  
1939  
    String command;
1940  
    if (isWindows())
1941  
      command = cmd + (append ? " >>" : " >") + winQuote(outFile.getPath()) + " 2>&1";
1942  
    else
1943  
      command = "nohup " + cmd + (append ? " >>" : " >") + bashQuote(outFile.getPath()) + " 2>&1 &";
1944  
    return command;
1945  
  }
1946  
1947  
  public static void mkdirsForFile(File file) {
1948  
    File dir = file.getParentFile();
1949  
    if (dir != null) // is null if file is in current dir
1950  
      dir.mkdirs();
1951  
  }
1952  
 
1953  
1954  
  static boolean portIsBound(int port) {
1955  
    try {
1956  
      ServerSocket s = new ServerSocket(port);
1957  
      s.close();
1958  
      return false;
1959  
    } catch (IOException e) {
1960  
      return true;
1961  
    }
1962  
  }
1963  
  
1964  
  static class LineBuf {
1965  
    StringBuffer buf = new StringBuffer();
1966  
    
1967  
    void append(String s) {
1968  
      buf.append(s);
1969  
    }
1970  
    
1971  
    String nextLine() {
1972  
      int i = buf.indexOf("\n");
1973  
      if (i >= 0) {
1974  
        String s = buf.substring(0, i > 0 && buf.charAt(i-1) == '\r' ? i-1 : i);
1975  
        buf.delete(0, i+1);
1976  
        return s;
1977  
      }
1978  
      return null;
1979  
    }
1980  
  } // LineBuf
1981  
1982  
static int chatSend_chatPort = 9751;
1983  
  static abstract class DialogIO {
1984  
    String line;
1985  
    boolean eos;
1986  
    
1987  
    abstract String readLineImpl();
1988  
    abstract boolean isStillConnected();
1989  
    abstract void sendLine(String line);
1990  
    abstract boolean isLocalConnection();
1991  
    abstract Socket getSocket();
1992  
    abstract void close();
1993  
    
1994  
    int getPort() { return getSocket().getPort(); }
1995  
    
1996  
    boolean helloRead;
1997  
    
1998  
    String readLineNoBlock() {
1999  
      String l = line;
2000  
      line = null;
2001  
      return l;
2002  
    }
2003  
    
2004  
    boolean waitForLine() { try {
2005  
 
2006  
      if (line != null) return true;
2007  
      //print("Readline");
2008  
      line = readLineImpl();
2009  
      //print("Readline done: " + line);
2010  
      if (line == null) eos = true;
2011  
      return line != null;
2012  
    
2013  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2014  
    
2015  
    String readLine() {
2016  
      waitForLine();
2017  
      helloRead = true;
2018  
      return readLineNoBlock();
2019  
    }
2020  
    
2021  
    String ask(String s, Object... args) {
2022  
      if (!helloRead) readLine();
2023  
      if (args.length != 0) s = format3(s, args);
2024  
      sendLine(s);
2025  
      return readLine();
2026  
    }
2027  
    
2028  
    String askLoudly(String s, Object... args) {
2029  
      if (!helloRead) readLine();
2030  
      if (args.length != 0) s = format3(s, args);
2031  
      print("> " + s);
2032  
      sendLine(s);
2033  
      String answer = readLine();
2034  
      print("< " + answer);
2035  
      return answer;
2036  
    }
2037  
    
2038  
    void pushback(String l) {
2039  
      if (line != null)
2040  
        throw fail();
2041  
      line = l;
2042  
      helloRead = false;
2043  
    }
2044  
  }
2045  
  
2046  
  static abstract class DialogHandler {
2047  
    abstract void run(DialogIO io);
2048  
  } // DialogIO
2049  
2050  
static DialogIO chatSend_dialog;
2051  
static String chatSend_id;
2052  
2053  
static void chatSend(String line) { try {
2054  
 
2055  
  if (chatSend_dialog == null) {
2056  
    chatSend_dialog = talkTo("localhost", chatSend_chatPort);    
2057  
    chatSend_dialog.waitForLine();
2058  
    String l = chatSend_dialog.readLineNoBlock();
2059  
    if (l.startsWith("Your ID: "))
2060  
      chatSend_id = l.substring("Your ID: ".length());
2061  
  }
2062  
2063  
  chatSend_dialog.sendLine(line);
2064  
2065  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} // chatSend
2066  
2067  
  static class TeeOutputStream extends OutputStream {
2068  
    
2069  
    protected OutputStream out, branch;
2070  
2071  
    public TeeOutputStream( OutputStream out, OutputStream branch ) {
2072  
      this.out = out;
2073  
      this.branch = branch;
2074  
    }
2075  
2076  
    @Override
2077  
    public synchronized void write(byte[] b) throws IOException {
2078  
      write(b, 0, b.length);
2079  
    }
2080  
2081  
    @Override
2082  
    public synchronized void write(byte[] b, int off, int len) throws IOException {
2083  
      //if (verbose) oldOut.println("Tee write " + new String(b, "UTF-8"));
2084  
      out.write(b, off, len);
2085  
      this.branch.write(b, off, len);
2086  
    }
2087  
2088  
    @Override
2089  
    public synchronized void write(int b) throws IOException {
2090  
      write(new byte[] {(byte) b});
2091  
    }
2092  
2093  
    /**
2094  
     * Flushes both streams.
2095  
     * @throws IOException if an I/O error occurs
2096  
     */
2097  
    @Override
2098  
    public void flush() throws IOException {
2099  
      out.flush();
2100  
      this.branch.flush();
2101  
    }
2102  
2103  
    /**
2104  
     * Closes both streams.
2105  
     * @throws IOException if an I/O error occurs
2106  
     */
2107  
    @Override
2108  
    public void close() throws IOException {
2109  
      out.close();
2110  
      this.branch.close();
2111  
    }
2112  
  }
2113  
  
2114  
  static boolean isChatServer(String[] args) {
2115  
    for (int i = 0; i < args.length; i++)
2116  
      if (isSnippetID(args[i]))
2117  
        return parseSnippetID(args[i]) == 1000867;
2118  
    return false;
2119  
  }
2120  
  
2121  
  static String getSnippetTitle(String id) {
2122  
    try {
2123  
      return loadPage(new URL(tb_mainServer() + "/tb-int/getfield.php?id=" + parseSnippetID(id) + "&field=title"));
2124  
    } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }
2125  
  }
2126  
  
2127  
  static void listUserThreadsWithStackTraces() {
2128  
    print("");
2129  
    Map<Thread, StackTraceElement[]> threadMap = Thread.getAllStackTraces();
2130  
    int n = 0;
2131  
    for (Thread t : threadMap.keySet()) {
2132  
      ThreadGroup g = t.getThreadGroup();
2133  
      if (g != null && g.getName().equals("system")) continue;
2134  
      ++n;
2135  
      print(t);
2136  
      for (StackTraceElement e : threadMap.get(t)) {
2137  
        print("  " + e);
2138  
      }
2139  
      print("");
2140  
    }
2141  
    print(n + " user threads.");
2142  
  }
2143  
  
2144  
  static void killMyself() {
2145  
    print("Killing myself. (insert overall feeling here)");
2146  
    System.exit(0);
2147  
  }
2148  
  
2149  
  static void makeVMAndroid() {
2150  
    Android3 a = new Android3("This is a JavaX VM.");
2151  
    a.responder = new Responder() {
2152  
      String answer(String s, List<String> history) {
2153  
        return x30.answer(s, history);
2154  
      }
2155  
    };
2156  
    a.daemon = true;
2157  
    a.console = false;
2158  
    a.incomingSilent = true; // to avoid too much printing
2159  
    a.useMultiPort = false;
2160  
    makeAndroid3(a);
2161  
    
2162  
    new MultiPort(); // auto-add multi-port
2163  
  }
2164  
  
2165  
  static class Info {
2166  
    String programID;
2167  
    String[] programArgs;
2168  
    File transpiledSrc;
2169  
    Class mainClass;
2170  
  }
2171  
  
2172  
  static Info info = new Info(); // hmm...
2173  
  
2174  
  // injectable info
2175  
  
2176  
  static List<PaA> injectable_programsInjected = new ArrayList<PaA>();
2177  
  static boolean injectable_on = true;
2178  
  static class PaA {
2179  
    String injectionID;
2180  
    String progID;
2181  
    String[] arguments;
2182  
    Class mainClass; // TODO: release eventually...
2183  
    WeakReference<Thread> mainThread;
2184  
    volatile boolean mainDone;
2185  
    volatile Throwable exception;
2186  
    
2187  
    PaA(String progID, String[] arguments) {
2188  
  this.arguments = arguments;
2189  
  this.progID = progID;}
2190  
    PaA() {}
2191  
  }
2192  
  
2193  
  static String vmID = makeRandomID(10);
2194  
  
2195  
  static synchronized void addInjection(PaA paa) {
2196  
    injectable_programsInjected.add(paa);
2197  
  }
2198  
  
2199  
  static synchronized void removeInjection(PaA paa) {
2200  
    cleanUp(paa.mainClass);
2201  
    injectable_programsInjected.remove(paa);
2202  
  }
2203  
  
2204  
  static synchronized List<PaA> getInjections() {
2205  
    return cloneList(injectable_programsInjected);
2206  
  }
2207  
  
2208  
  static String answer(String s, List<String> history) { try {
2209  
 
2210  
    Matches m = new Matches();
2211  
    
2212  
    if (match3("kill!", s)) {
2213  
      killMyself();
2214  
      return "ok";
2215  
    }
2216  
    if (match3("What is your process ID?", s) || match3("what is your pid?", s))
2217  
      return getPID();
2218  
    if (match3("get vm id", s))
2219  
      return vmID;
2220  
    if (match3("what is the javax program id?", s))
2221  
      return javaxProgramID;
2222  
    if (match3("what is the main program id?", s))
2223  
      return info.programID;
2224  
    if (match3("what are your program arguments?", s))
2225  
      return structure(info.programArgs);
2226  
    if (match3("get fields of main class", s))
2227  
      return structure(listFields(info.mainClass));
2228  
    if (match3("get field * of main class", s, m))
2229  
      return structure(get(info.mainClass, m.m[0]));
2230  
    if (match3("invoke function * of main class", s, m))
2231  
      return structure(call(info.mainClass, m.m[0]));
2232  
    if (match3("set field * of main class to *", s, m)) {
2233  
      set(info.mainClass, m.m[0], unstructure(m.m[1]));
2234  
      return "ok";
2235  
    }
2236  
    if (match3("how much memory are you consuming", s))
2237  
      return "Java heap size: " + (Runtime.getRuntime().totalMemory()+1024*1024-1)/1024/1024 + " MB";
2238  
    if (match3("how much memory is used after GC?", s)) {
2239  
      System.gc();
2240  
      return "Java heap used: " + (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()+1024*1024-1)/1024/1024 + " MB";
2241  
    }
2242  
    if (match3("how much memory is used?", s))
2243  
      return "Java heap used: " + (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()+1024*1024-1)/1024/1024 + " MB";
2244  
      
2245  
    if (match3("please inject program *", s, m) || match3("please inject program * with arguments *", s, m)) {
2246  
      synchronized(x30.class) {
2247  
        final String progID = formatSnippetID(unquote(m.m[0]));
2248  
        final String[] arguments = m.m.length > 1 ? toStringArray(unstructure(unquote(m.m[1]))) : new String[0];
2249  
        final PaA paa = new PaA(progID, arguments);
2250  
        paa.injectionID = randomID(8);
2251  
        addInjection(paa);
2252  
    
2253  
        // better call JavaX for translation in a single thread.
2254  
        paa.mainClass = hotwire(progID);
2255  
    
2256  
        // program may run in its own thread.
2257  
        { Thread _t_0 = new Thread(progID) {
2258  
public void run() {
2259  
try  {
2260  
          paa.mainThread = new WeakReference(currentThread());
2261  
          try {
2262  
            callMain(paa.mainClass, arguments);
2263  
          } catch (Throwable e) {
2264  
            paa.exception = e;
2265  
            e.printStackTrace();
2266  
          } finally {
2267  
            paa.mainDone = true;
2268  
            synchronized(x30.class) {}
2269  
          }
2270  
        } catch (Exception _e) {
2271  
  throw _e instanceof RuntimeException ? (RuntimeException) _e : new RuntimeException(_e); } }
2272  
};
2273  
_t_0.start(); }
2274  
  
2275  
        return format3("OK. Injection ID: *", paa.injectionID);
2276  
      }
2277  
    }
2278  
    
2279  
    if (match3("get injection exception *", s, m)) {
2280  
      String injectionID = unquote(m.m[0]);
2281  
      PaA paa = findInjection(injectionID);
2282  
      if (paa == null)
2283  
        return "Sorry. Injection not found";
2284  
      return "OK: " + paa.exception == null ? "no exception" : getStackTrace(paa.exception);
2285  
    }
2286  
     
2287  
    if (match3("get injection * variable *", s, m)) {
2288  
      String injectionID = unquote(m.m[0]);
2289  
      String var = unquote(m.m[1]);
2290  
      PaA paa = findInjection(injectionID);
2291  
      if (paa == null)
2292  
        return "Sorry. Injection not found";
2293  
      return "OK: " + structure(getOpt(paa.mainClass, var));
2294  
    }
2295  
     
2296  
    if (match3("get injection result *", s, m)) {
2297  
      String injectionID = unquote(m.m[0]);
2298  
      PaA paa = findInjection(injectionID);
2299  
      if (paa == null)
2300  
        return "Sorry. Injection not found";
2301  
      return "OK: " + structure(getOpt(paa.mainClass, "result"));
2302  
    }
2303  
     
2304  
    if (match3("is injection's * main done", s, m)) {
2305  
      String injectionID = unquote(m.m[0]);
2306  
      PaA paa = findInjection(injectionID);
2307  
      if (paa == null)
2308  
        return "Sorry. Injection not found";
2309  
      return paa.mainDone ? "Yes." : "No.";
2310  
    }
2311  
    
2312  
    if (match3("get injections", s, m)) {
2313  
      return structure(getInjections());
2314  
    }
2315  
    
2316  
    if (match3("remove injection *", s, m)) {
2317  
      String injectionID = unquote(m.m[0]);
2318  
      PaA paa = findInjection(injectionID);
2319  
      if (paa == null)
2320  
        return "Sorry. Injection not found";
2321  
      removeInjection(paa);
2322  
      return "OK, removed.";
2323  
    }
2324  
    
2325  
    if (match3("which programs are you running (ids only)", s, m)) {
2326  
      synchronized(x30.class) {
2327  
        List<String> l = new ArrayList<String>();
2328  
        for (String progID : instances.keySet())
2329  
          if (getInstance(progID) != null)
2330  
            l.add(progID);
2331  
        return format3("these: *", structure(l));
2332  
      }
2333  
    }
2334  
    
2335  
    List multiPorts = getMultiPorts();
2336  
    if (!multiPorts.isEmpty()) {
2337  
      Object multiPort = multiPorts.get(0);
2338  
      String answer = makeResponder(multiPort).answer(s, history);
2339  
      if (answer != null) return answer;
2340  
    }
2341  
    
2342  
    if (match3("list bots", s))
2343  
      return structure(litmap());
2344  
      
2345  
    if (match3("get console output", s))
2346  
      return "ok " + quote((String) call(bootUpClass, "getConsoleOutput"));
2347  
    
2348  
    return null;
2349  
  
2350  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2351  
  
2352  
  static synchronized PaA findInjection(String injectionID) {
2353  
    for (PaA paa : injectable_programsInjected)
2354  
      if (eq(paa.injectionID, injectionID))
2355  
        return paa;
2356  
    return null;
2357  
  }
2358  
static int makeAndroid(String greeting) {
2359  
  return makeAndroid3(greeting).port;
2360  
}
2361  
2362  
static void makeAndroid(Android3 a) {
2363  
  makeAndroid3(a);
2364  
} // makeAndroid / makeAndroidNoConsole
2365  
  static void setOpt(Object o, String field, Object value) {
2366  
    if (o instanceof Class) setOpt((Class) o, field, value);
2367  
    else try {
2368  
      Field f = setOpt_findField(o.getClass(), field);
2369  
      if (f != null)
2370  
        smartSet(f, o, value);
2371  
    } catch (Exception e) {
2372  
      throw new RuntimeException(e);
2373  
    }
2374  
  }
2375  
  
2376  
  static void setOpt(Class c, String field, Object value) {
2377  
    try {
2378  
      Field f = setOpt_findStaticField(c, field);
2379  
      if (f != null)
2380  
        smartSet(f, null, value);
2381  
    } catch (Exception e) {
2382  
      throw new RuntimeException(e);
2383  
    }
2384  
  }
2385  
  
2386  
  static Field setOpt_findField(Class<?> c, String field) {
2387  
    for (Field f : c.getDeclaredFields())
2388  
      if (f.getName().equals(field))
2389  
        return f;
2390  
    return null;
2391  
  }
2392  
  
2393  
  static Field setOpt_findStaticField(Class<?> c, String field) {
2394  
    for (Field f : c.getDeclaredFields())
2395  
      if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0)
2396  
        return f;
2397  
    return null;
2398  
  } // setOpt
2399  
// get purpose 1: access a list/array (safer version of x.get(y))
2400  
2401  
static <A> A get(List<A> l, int idx) {
2402  
  return idx >= 0 && idx < l(l) ? l.get(idx) : null;
2403  
}
2404  
2405  
static <A> A get(A[] l, int idx) {
2406  
  return idx >= 0 && idx < l(l) ? l[idx] : null;
2407  
}
2408  
2409  
// get purpose 2: access a field by reflection or a map
2410  
2411  
static Object get(Object o, String field) {
2412  
  if (o instanceof Class) return get((Class) o, field);
2413  
  
2414  
  if (o instanceof Map)
2415  
    return ((Map) o).get(field);
2416  
    
2417  
  if (o.getClass().getName().equals("main$DynamicObject"))
2418  
    return call(get_raw(o, "fieldValues"), "get", field);
2419  
    
2420  
  return get_raw(o, field);
2421  
}
2422  
2423  
static Object get_raw(Object o, String field) {
2424  
  try {
2425  
    Field f = get_findField(o.getClass(), field);
2426  
    f.setAccessible(true);
2427  
    return f.get(o);
2428  
  } catch (Exception e) {
2429  
    throw new RuntimeException(e);
2430  
  }
2431  
}
2432  
2433  
static Object get(Class c, String field) {
2434  
  try {
2435  
    Field f = get_findStaticField(c, field);
2436  
    f.setAccessible(true);
2437  
    return f.get(null);
2438  
  } catch (Exception e) {
2439  
    throw new RuntimeException(e);
2440  
  }
2441  
}
2442  
2443  
static Field get_findStaticField(Class<?> c, String field) {
2444  
  Class _c = c;
2445  
  do {
2446  
    for (Field f : _c.getDeclaredFields())
2447  
      if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0)
2448  
        return f;
2449  
    _c = _c.getSuperclass();
2450  
  } while (_c != null);
2451  
  throw new RuntimeException("Static field '" + field + "' not found in " + c.getName());
2452  
}
2453  
2454  
static Field get_findField(Class<?> c, String field) {
2455  
  Class _c = c;
2456  
  do {
2457  
    for (Field f : _c.getDeclaredFields())
2458  
      if (f.getName().equals(field))
2459  
        return f;
2460  
    _c = _c.getSuperclass();
2461  
  } while (_c != null);
2462  
  throw new RuntimeException("Field '" + field + "' not found in " + c.getName());
2463  
} // get
2464  
static Class getMainClass() { try {
2465  
 
2466  
  return Class.forName("main");
2467  
2468  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2469  
2470  
static Class getMainClass(Object o) { try {
2471  
 
2472  
  return (o instanceof Class ? (Class) o : o.getClass()).getClassLoader().loadClass("main");
2473  
2474  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} // getMainClass
2475  
static DialogIO talkTo(int port) {
2476  
  return talkTo("localhost", port);
2477  
}
2478  
2479  
static DialogIO talkTo(String ip, int port) { try {
2480  
 
2481  
  final Socket s = new Socket(ip, port);    
2482  
  //print("Talking to " + ip + ":" + port);
2483  
2484  
  final Writer w = new OutputStreamWriter(s.getOutputStream(), "UTF-8");
2485  
  final BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
2486  
  return new DialogIO() {
2487  
    boolean isLocalConnection() {
2488  
      return s.getInetAddress().isLoopbackAddress();
2489  
    }
2490  
    
2491  
    boolean isStillConnected() {
2492  
      return !(eos || s.isClosed());
2493  
    }
2494  
    
2495  
    void sendLine(String line) { try {
2496  
 
2497  
      w.write(line + "\n");
2498  
      w.flush();
2499  
    
2500  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2501  
    
2502  
    String readLineImpl() { try {
2503  
 
2504  
      return in.readLine();
2505  
    
2506  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2507  
    
2508  
    void close() {
2509  
      try {
2510  
        s.close();
2511  
      } catch (IOException e) {
2512  
        // whatever
2513  
      }
2514  
    }
2515  
    
2516  
    Socket getSocket() {
2517  
      return s;
2518  
    }
2519  
  };
2520  
2521  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} // talkTo
2522  
static String[] toStringArray(List<String> list) {
2523  
  return list.toArray(new String[list.size()]);
2524  
}
2525  
2526  
static String[] toStringArray(Object o) {
2527  
  if (o instanceof String[])
2528  
    return (String[]) o;
2529  
  else if (o instanceof List)
2530  
    return toStringArray((List<String>) o);
2531  
  else
2532  
    throw fail("Not a list or array: " + structure(o));
2533  
}
2534  
 // toStringArray
2535  
  
2536  
static byte[] loadDataSnippetImpl(String snippetID) throws IOException {
2537  
  byte[] data;
2538  
  try {
2539  
    URL url = new URL("http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_"
2540  
      + parseSnippetID(snippetID) + "&contentType=application/binary");
2541  
    System.err.println("Loading library: " + url);
2542  
    try {
2543  
      data = loadBinaryPage(url.openConnection());
2544  
    } catch (IOException e) {
2545  
      data = null;
2546  
    }
2547  
    
2548  
    if (data == null || data.length == 0) {
2549  
      url = new URL("http://data.tinybrain.de/blobs/"
2550  
        + parseSnippetID(snippetID));
2551  
      System.err.println("Loading library: " + url);
2552  
      data = loadBinaryPage(url.openConnection());
2553  
    }
2554  
    System.err.println("Bytes loaded: " + data.length);
2555  
  } catch (FileNotFoundException e) {
2556  
    throw new IOException("Binary snippet #" + snippetID + " not found or not public");
2557  
  }
2558  
  return data;
2559  
}
2560  
  static AtomicBoolean readLine_used = new AtomicBoolean();
2561  
  static BufferedReader readLine_reader;
2562  
2563  
  static String readLine() { try {
2564  
 
2565  
    if (!readLine_used.compareAndSet(false, true))
2566  
      throw fail("readLine is in use.");
2567  
    try {
2568  
      while (true) {
2569  
        if (readLine_reader == null)
2570  
          readLine_reader = new BufferedReader(new InputStreamReader(System.in, "UTF-8")); // XX - is that right?
2571  
          
2572  
        if (!readLine_reader.ready())
2573  
          sleep(100);
2574  
        else {
2575  
          String s = readLine_reader.readLine();
2576  
          if (s != null) {
2577  
            print(s);
2578  
            return s;
2579  
          }
2580  
        }
2581  
      }
2582  
    } finally {
2583  
      readLine_used.set(false);
2584  
    }
2585  
  
2586  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2587  
  
2588  
  static HashMap<String, WeakReference> weakrefs = new HashMap<String, WeakReference>();
2589  
  // static WeakIdentityHashMap<O, S> reverseWeakrefs = new WeakIdentityHashMap<O, S>();
2590  
  static long weakRefCounter;
2591  
  
2592  
  // TODO: lookup in reverse map
2593  
  static synchronized String weakref(Object o) {
2594  
    if (o == null) return "null";
2595  
    String id = vmID + "/" + ++weakRefCounter;
2596  
    weakrefs.put(id, new WeakReference(o));
2597  
    return id;
2598  
  }
2599  
2600  
  static synchronized Object getRef(String id) {
2601  
    // TODO: clean up the list some time
2602  
    
2603  
    WeakReference ref = weakrefs.get(id);
2604  
    if (ref == null) return null;
2605  
    return ref.get();
2606  
  }
2607  
  
2608  
  static List<Object> multiPorts = new ArrayList<Object>();
2609  
  
2610  
  static synchronized List<Object> getMultiPorts() {
2611  
    return cloneList(multiPorts);
2612  
  }
2613  
  
2614  
  // true if you're the first one
2615  
  static synchronized boolean addMultiPort(Object o) {
2616  
    multiPorts.add(o);
2617  
    /*if (multiPorts.size() == 1) {
2618  
      { Thread _t_1 = new Thread("keep alive") {
2619  
public void run() {
2620  
try  { x30.sleep(); } catch (Exception _e) {
2621  
  throw _e instanceof RuntimeException ? (RuntimeException) _e : new RuntimeException(_e); } }
2622  
};
2623  
_t_1.start(); } // keep VM alive since there is a multiport
2624  
    }*/
2625  
    return multiPorts.size() == 1;
2626  
  }
2627  
  
2628  
  static synchronized void removeMultiPort(Object o) {
2629  
    multiPorts.remove(o);
2630  
  }
2631  
  
2632  
  static String getInjectionID(Class mainClass) {
2633  
    List<PaA> l = getInjections();
2634  
    for (PaA injection : l)
2635  
      if (injection.mainClass == mainClass)
2636  
        return injection.injectionID;
2637  
    return null;
2638  
  }
2639  
  
2640  
  static String getInjectionID() { return null; } // used somewhere...
2641  
  
2642  
  static void sleep(long ms) {
2643  
    try {
2644  
      Thread.sleep(ms);
2645  
    } catch (Exception e) { throw new RuntimeException(e); }
2646  
  }
2647  
2648  
  static void sleep() { try {
2649  
 
2650  
    print("Sleeping.");
2651  
    synchronized(x30.class) { x30.class.wait(); }
2652  
  
2653  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2654  
  
2655  
  static WeakHashMap<Class, String> classToSourceCode = new WeakHashMap<Class, String>();
2656  
  
2657  
  synchronized static void registerSourceCode(Class c, String src) {
2658  
    classToSourceCode.put(c, src);
2659  
  }
2660  
  
2661  
  synchronized static String getSourceCodeForClass(Class c) {
2662  
    return classToSourceCode.get(c);
2663  
  }
2664  
  static class Matches {
2665  
    String[] m;
2666  
    String get(int i) { return m[i]; }
2667  
    String unq(int i) { return unquote(m[i]); }
2668  
    String fsi(int i) { return formatSnippetID(unq(i)); }
2669  
    String fsi() { return fsi(0); }
2670  
    String tlc(int i) { return unq(i).toLowerCase(); }
2671  
    boolean bool(int i) { return "true".equals(unq(i)); }
2672  
    String rest() { return m[m.length-1]; } // for matchStart
2673  
    int psi(int i) { return Integer.parseInt(unq(i)); }
2674  
  }
2675  
 // Matches
2676  
  
2677  
  // get main class of first program (for console)
2678  
  static Object getMainMainClass() {
2679  
    PaA paa = first(getInjections());
2680  
    return paa == null ? null : paa.mainClass;
2681  
  }
2682  
  
2683  
  static boolean _inCore() {
2684  
    return true;
2685  
  }
2686  
2687  
// This is for main classes that are all static.
2688  
// (We don't go to base classes.)
2689  
static Set<String> listFields(Object c) {
2690  
  TreeSet<String> fields = new TreeSet<String>();
2691  
  for (Field f : ((Class) c).getDeclaredFields())
2692  
    fields.add(f.getName());
2693  
  return fields;
2694  
}
2695  
static Thread currentThread() {
2696  
  return Thread.currentThread();
2697  
}
2698  
static <A> List<A> cloneList(Collection<A> l) {
2699  
  //O mutex = getOpt(l, "mutex");
2700  
  /*if (mutex != null)
2701  
    synchronized(mutex) {
2702  
      ret new ArrayList<A>(l);
2703  
    }
2704  
  else
2705  
    ret new ArrayList<A>(l);*/
2706  
  // assume mutex is equal to collection, which will be true unless you explicitly pass a mutex to synchronizedList() which no one ever does.
2707  
  synchronized(l) {
2708  
    return new ArrayList<A>(l);
2709  
  }
2710  
}
2711  
static Map litmap(Object... x) {
2712  
  TreeMap map = new TreeMap();
2713  
  litmap_impl(map, x);
2714  
  return map;
2715  
}
2716  
2717  
static void litmap_impl(Map map, Object... x) {
2718  
  for (int i = 0; i < x.length-1; i += 2)
2719  
    if (x[i+1] != null)
2720  
      map.put(x[i], x[i+1]);
2721  
}
2722  
  static RuntimeException fail() {
2723  
    throw new RuntimeException("fail");
2724  
  }
2725  
  
2726  
  static RuntimeException fail(Object msg) {
2727  
    throw new RuntimeException(String.valueOf(msg));
2728  
  }
2729  
  
2730  
  static RuntimeException fail(String msg) {
2731  
    throw new RuntimeException(unnull(msg));
2732  
  }
2733  
   
2734  
  // disabled for now to shorten some programs 
2735  
  /*static RuntimeException fail(S msg, O... args) {
2736  
    throw new RuntimeException(format(msg, args));
2737  
  }*/
2738  
static void cleanJavaXCache(int retentionHours, boolean verbose) {
2739  
  try {
2740  
    if (verbose)
2741  
      System.out.println("Cleaning JavaX Cache (" + retentionHours + " hours retention time)");
2742  
    int fileDeletions = 0;
2743  
    for (String userDir : litlist(userHome(), userHomeInternal())) {
2744  
      File javax = new File(userHome(), ".javax");
2745  
      long now = now();
2746  
      File[] files = javax.listFiles();
2747  
      if (files != null) for (File dir : files) {
2748  
        if (dir.isDirectory() && Pattern.compile("\\d+").matcher(dir.getName()).matches()) {
2749  
          long time = Long.parseLong(dir.getName());
2750  
          long seconds = (now - time) / 1000;
2751  
          long minutes = seconds / 60;
2752  
          long hours = minutes / 60;
2753  
          if (hours >= retentionHours) {
2754  
            //System.out.println("Can delete " + dir.getAbsolutePath() + ", age: " + hours + " h");
2755  
            fileDeletions += cleanJavaXCache_removeDir(dir, verbose);
2756  
          }
2757  
        }
2758  
      }
2759  
    }
2760  
    if (verbose && fileDeletions != 0)
2761  
      print("Cleaned cache. File deletions: " + fileDeletions);
2762  
  } catch (Throwable __e) { printStackTrace(__e); }
2763  
}
2764  
2765  
static int cleanJavaXCache_removeDir(File dir, boolean verbose) {
2766  
  int fileDeletions = 0;
2767  
  if (dir.getAbsolutePath().indexOf(".javax") < 0)  // security check!
2768  
    throw fail("WHAT ARE YOU DOING!? >> " + dir.getAbsolutePath());
2769  
  for (File f : dir.listFiles()) {
2770  
    if (f.isDirectory())
2771  
      cleanJavaXCache_removeDir(f, verbose);
2772  
    else {
2773  
      if (verbose)
2774  
        print("Deleting " + f.getAbsolutePath());
2775  
      f.delete();
2776  
      ++fileDeletions;
2777  
    }
2778  
  }
2779  
  dir.delete();
2780  
  return fileDeletions;
2781  
}
2782  
2783  
static String fsi(String id) {
2784  
  return formatSnippetID(id);
2785  
}
2786  
 static String boss(String line) {
2787  
    try {
2788  
      //S s = sendToLocalBotOpt("Boss Bot", line);
2789  
      DialogIO io = talkTo(4990); // Boss Bot port
2790  
      io.readLine();
2791  
      io.sendLine(line);
2792  
      String s = io.readLine();
2793  
      Matches m = new Matches();
2794  
      if (match3("text: *", s, m))
2795  
        return unquote(m.m[0]);
2796  
      return null;
2797  
    } catch (Exception e) {
2798  
      //e.printStackTrace();
2799  
      return null;
2800  
    }
2801  
  }
2802  
static class DynamicObject {
2803  
  String className;
2804  
  Map<String, Object> fieldValues = new TreeMap<String, Object>();
2805  
}
2806  
2807  
static Object unstructure(String text) {
2808  
  return unstructure(text, false);
2809  
}
2810  
2811  
// TODO: backrefs for hashmap{} etc
2812  
static Object unstructure(String text, final boolean allDynamic) {
2813  
  if (text == null) return null;
2814  
  final List<String> tok = javaTok(text);
2815  
  final boolean debug = unstructure_debug;
2816  
  
2817  
  class X {
2818  
    int i = 1;
2819  
    HashMap<Integer, Object> refs = new HashMap<Integer, Object>();
2820  
2821  
    Object parse() {
2822  
      String t = tok.get(i);
2823  
      
2824  
      int refID = 0;
2825  
      if (t.startsWith("m") && isInteger(t.substring(1))) {
2826  
        refID = parseInt(t.substring(1));
2827  
        i += 2;
2828  
        t = tok.get(i);
2829  
      }
2830  
      
2831  
      if (debug)
2832  
        print("parse: " + quote(t));
2833  
        
2834  
      if (t.startsWith("\"")) {
2835  
        String s = unquote(tok.get(i));
2836  
        i += 2;
2837  
        return s;
2838  
      }
2839  
      if (t.startsWith("'")) {
2840  
        char c = unquoteCharacter(tok.get(i));
2841  
        i += 2;
2842  
        return c;
2843  
      }
2844  
      if (t.equals("bigint"))
2845  
        return parseBigInt();
2846  
      if (t.equals("d"))
2847  
        return parseDouble();
2848  
      if (t.equals("false") || t.equals("f")) {
2849  
        i += 2; return false;
2850  
      }
2851  
      if (t.equals("true") || t.equals("t")) {
2852  
        i += 2; return true;
2853  
      }
2854  
      if (t.equals("-")) {
2855  
        t = tok.get(i+2);
2856  
        i += 4;
2857  
        return isLongConstant(t) ? (Object) (-parseLong(t)) : (Object) (-parseInt(t));
2858  
      }
2859  
      if (isInteger(t) || isLongConstant(t)) {
2860  
        i += 2;
2861  
        if (debug)
2862  
          print("isLongConstant " + quote(t) + " => " + isLongConstant(t));
2863  
        if (isLongConstant(t)) return parseLong(t);
2864  
        long l = parseLong(t);
2865  
        return l != (int) l ? new Long(l) : new Integer((int) l);
2866  
      }
2867  
      
2868  
      if (t.equals("File")) {
2869  
        File f = new File(unquote(tok.get(i+2)));
2870  
        i += 4;
2871  
        return f;
2872  
      }
2873  
      
2874  
      if (t.startsWith("r") && isInteger(t.substring(1))) {
2875  
        i += 2;
2876  
        int ref = Integer.parseInt(t.substring(1));
2877  
        Object o = refs.get(ref);
2878  
        if (o == null)
2879  
          print("Warning: unsatisfied back reference " + ref);
2880  
        return o;
2881  
      }
2882  
      
2883  
      return parse_inner(refID);
2884  
    }
2885  
    
2886  
    // everything that can be backreferenced
2887  
    Object parse_inner(int refID) {
2888  
      String t = tok.get(i);
2889  
      
2890  
      if (debug)
2891  
        print("parse_inner: " + quote(t));
2892  
        
2893  
      if (t.equals("hashset"))
2894  
        return parseHashSet();
2895  
      if (t.equals("treeset"))
2896  
        return parseTreeSet();
2897  
      if (t.equals("hashmap"))
2898  
        return parseHashMap();
2899  
      if (t.equals("{"))
2900  
        return parseMap();
2901  
      if (t.equals("["))
2902  
        return parseList();
2903  
      if (t.equals("array"))
2904  
        return parseArray();
2905  
      if (t.equals("class"))
2906  
        return parseClass();
2907  
      if (t.equals("l"))
2908  
        return parseLisp();
2909  
      if (t.equals("null")) {
2910  
        i += 2; return null;
2911  
      }
2912  
      if (isJavaIdentifier(t)) {
2913  
        Class c = allDynamic ? null : findClass(t);
2914  
        DynamicObject dO = null;
2915  
        Object o = null;
2916  
        if (c != null)
2917  
          o = nuObject(c);
2918  
        else {
2919  
          dO = new DynamicObject();
2920  
          dO.className = t;
2921  
        }
2922  
        
2923  
        if (refID != 0)
2924  
          refs.put(refID, o);
2925  
          
2926  
        i += 2;
2927  
        if (i < tok.size() && tok.get(i).equals("(")) {
2928  
          consume("(");
2929  
          while (!tok.get(i).equals(")")) {
2930  
            // It's like parsing a map.
2931  
            //Object key = parse();
2932  
            //if (tok.get(i).equals(")"))
2933  
            //  key = onlyField();
2934  
            String key = unquote(tok.get(i));
2935  
            i += 2;
2936  
            consume("=");
2937  
            Object value = parse();
2938  
            if (o != null)
2939  
              setOpt(o, key, value);
2940  
            else
2941  
              dO.fieldValues.put(key, value);
2942  
            if (tok.get(i).equals(",")) i += 2;
2943  
          }
2944  
          consume(")");
2945  
        }
2946  
        return o != null ? o : dO;
2947  
      }
2948  
      throw new RuntimeException("Unknown token " + (i+1) + ": " + t);
2949  
    }
2950  
    
2951  
    Object parseSet(Set set) {
2952  
      set.addAll((List) parseList());
2953  
      return set;
2954  
    }
2955  
    
2956  
    Object parseLisp() {
2957  
      consume("l");
2958  
      consume("(");
2959  
      List list = new ArrayList();
2960  
      while (!tok.get(i).equals(")")) {
2961  
        list.add(parse());
2962  
        if (tok.get(i).equals(",")) i += 2;
2963  
      }
2964  
      consume(")");
2965  
      return newObject("main$Lisp", (String) list.get(0), subList(list, 1));
2966  
    }
2967  
    
2968  
    Object parseList() {
2969  
      consume("[");
2970  
      List list = new ArrayList();
2971  
      while (!tok.get(i).equals("]")) {
2972  
        list.add(parse());
2973  
        if (tok.get(i).equals(",")) i += 2;
2974  
      }
2975  
      consume("]");
2976  
      return list;
2977  
    }
2978  
    
2979  
    Object parseArray() {
2980  
      consume("array");
2981  
      consume("{");
2982  
      List list = new ArrayList();
2983  
      while (!tok.get(i).equals("}")) {
2984  
        list.add(parse());
2985  
        if (tok.get(i).equals(",")) i += 2;
2986  
      }
2987  
      consume("}");
2988  
      return list.toArray();
2989  
    }
2990  
    
2991  
    Object parseClass() {
2992  
      consume("class");
2993  
      consume("(");
2994  
      String name = tok.get(i);
2995  
      i += 2;
2996  
      consume(")");
2997  
      Class c = allDynamic ? null : findClass(name);
2998  
      if (c != null) return c;
2999  
      DynamicObject dO = new DynamicObject();
3000  
      dO.className = "java.lang.Class";
3001  
      dO.fieldValues.put("name", name);
3002  
      return dO;
3003  
    }
3004  
    
3005  
    Object parseBigInt() {
3006  
      consume("bigint");
3007  
      consume("(");
3008  
      String val = tok.get(i);
3009  
      i += 2;
3010  
      if (eq(val, "-")) {
3011  
        val = "-" + tok.get(i);
3012  
        i += 2;
3013  
      }
3014  
      consume(")");
3015  
      return new BigInteger(val);
3016  
    }
3017  
    
3018  
    Object parseDouble() {
3019  
      consume("d");
3020  
      consume("(");
3021  
      String val = unquote(tok.get(i));
3022  
      i += 2;
3023  
      consume(")");
3024  
      return Double.parseDouble(val);
3025  
    }
3026  
    
3027  
    Object parseHashMap() {
3028  
      consume("hashmap");
3029  
      return parseMap(new HashMap());
3030  
    }
3031  
    
3032  
    Object parseHashSet() {
3033  
      consume("hashset");
3034  
      return parseSet(new HashSet());
3035  
    }
3036  
    
3037  
    Object parseTreeSet() {
3038  
      consume("treeset");
3039  
      return parseSet(new TreeSet());
3040  
    }
3041  
    
3042  
    Object parseMap() {
3043  
      return parseMap(new TreeMap());
3044  
    }
3045  
    
3046  
    Object parseMap(Map map) {
3047  
      consume("{");
3048  
      while (!tok.get(i).equals("}")) {
3049  
        Object key = parse();
3050  
        consume("=");
3051  
        Object value = parse();
3052  
        map.put(key, value);
3053  
        if (tok.get(i).equals(",")) i += 2;
3054  
      }
3055  
      consume("}");
3056  
      return map;
3057  
    }
3058  
    
3059  
    void consume(String s) {
3060  
      if (!tok.get(i).equals(s)) {
3061  
        String prevToken = i-2 >= 0 ? tok.get(i-2) : "";
3062  
        String nextTokens = join(tok.subList(i, Math.min(i+4, tok.size())));
3063  
        throw fail(quote(s) + " expected: " + prevToken + " " + nextTokens + " (" + i + "/" + tok.size() + ")");
3064  
      }
3065  
      i += 2;
3066  
    }
3067  
  }
3068  
  
3069  
  return new X().parse();
3070  
}
3071  
3072  
static boolean unstructure_debug;
3073  
static <A> ArrayList<A> litlist(A... a) {
3074  
  return new ArrayList<A>(Arrays.asList(a));
3075  
}
3076  
  static ThreadLocal<String> loadPage_charset = new ThreadLocal<String>();
3077  
  static boolean loadPage_allowGzip = true, loadPage_debug;
3078  
  static boolean loadPage_anonymous; // don't send computer ID
3079  
  static int loadPage_verboseness = 100000;
3080  
3081  
  public static String loadPageSilently(String url) {
3082  
    try {
3083  
      return loadPageSilently(new URL(loadPage_preprocess(url)));
3084  
    } catch (IOException e) { throw new RuntimeException(e); }
3085  
  }
3086  
3087  
  public static String loadPageSilently(URL url) {
3088  
    try {
3089  
      IOException e = null;
3090  
      for (int tries = 0; tries < 60; tries++)
3091  
        try {
3092  
          URLConnection con = url.openConnection();
3093  
          return loadPage(con, url);
3094  
        } catch (IOException _e) {
3095  
          e = _e;
3096  
          print("Retrying because of: " + e);
3097  
          sleepSeconds(1);
3098  
        }
3099  
      throw e;
3100  
    } catch (IOException e) { throw new RuntimeException(e); }
3101  
  }
3102  
3103  
  static String loadPage_preprocess(String url) {  
3104  
    if (url.startsWith("tb/"))
3105  
      url = tb_mainServer() + url;
3106  
    if (url.indexOf("://") < 0)
3107  
      url = "http://" + url;
3108  
    return url;
3109  
  }
3110  
  
3111  
  public static String loadPage(String url) {
3112  
    try {
3113  
      return loadPage(new URL(loadPage_preprocess(url)));
3114  
    } catch (IOException e) { throw new RuntimeException(e); }
3115  
  }
3116  
  
3117  
  public static String loadPage(URL url) {
3118  
    print("Loading: " + hideCredentials(url.toExternalForm()));
3119  
    return loadPageSilently(url);
3120  
  }
3121  
3122  
  public static String loadPage(URLConnection con, URL url) throws IOException {
3123  
    try {
3124  
      if (!loadPage_anonymous) {
3125  
        String computerID = getComputerID();
3126  
        if (computerID != null)
3127  
          con.setRequestProperty("X-ComputerID", computerID);
3128  
      }
3129  
      if (loadPage_allowGzip)
3130  
        con.setRequestProperty("Accept-Encoding", "gzip");
3131  
    } catch (Throwable e) {} // fails if within doPost
3132  
    String contentType = con.getContentType();
3133  
    if (contentType == null)
3134  
      throw new IOException("Page could not be read: " + url);
3135  
    //print("Content-Type: " + contentType);
3136  
    String charset = loadPage_charset == null ? null : loadPage_charset.get();
3137  
    if (charset == null) charset = loadPage_guessCharset(contentType);
3138  
    
3139  
    InputStream in = con.getInputStream();
3140  
    if ("gzip".equals(con.getContentEncoding())) {
3141  
      if (loadPage_debug)
3142  
        print("loadPage: Using gzip.");
3143  
      in = new GZIPInputStream(in);
3144  
    }
3145  
    Reader r = new InputStreamReader(in, charset);
3146  
    
3147  
    StringBuilder buf = new StringBuilder();
3148  
    int n = 0;
3149  
    while (true) {
3150  
      int ch = r.read();
3151  
      if (ch < 0)
3152  
        break;
3153  
      buf.append((char) ch);
3154  
      ++n;
3155  
      if ((n % loadPage_verboseness) == 0) print("  " + n + " chars read");
3156  
    }
3157  
    return buf.toString();
3158  
  }
3159  
  
3160  
  static String loadPage_guessCharset(String contentType) {
3161  
    Pattern p = Pattern.compile("text/[a-z]+;\\s+charset=([^\\s]+)\\s*");
3162  
    Matcher m = p.matcher(contentType);
3163  
    /* If Content-Type doesn't match this pre-conception, choose default and hope for the best. */
3164  
    return m.matches() ? m.group(1) : "ISO-8859-1";
3165  
  }
3166  
3167  
static int l(Object[] array) {
3168  
  return array == null ? 0 : array.length;
3169  
}
3170  
3171  
static int l(byte[] array) {
3172  
  return array == null ? 0 : array.length;
3173  
}
3174  
3175  
static int l(int[] array) {
3176  
  return array == null ? 0 : array.length;
3177  
}
3178  
3179  
static int l(char[] array) {
3180  
  return array == null ? 0 : array.length;
3181  
}
3182  
3183  
static int l(Collection c) {
3184  
  return c == null ? 0 : c.size();
3185  
}
3186  
3187  
static int l(Map m) {
3188  
  return m == null ? 0 : m.size();
3189  
}
3190  
3191  
static int l(String s) {
3192  
  return s == null ? 0 : s.length();
3193  
} 
3194  
3195  
3196  
static String getServerTranspiled2(String id) {
3197  
  String transpiled = loadCachedTranspilation(id);
3198  
  String md5 = null;
3199  
  try { /* pcall 1*/ 
3200  
  if (transpiled != null)
3201  
    md5 = md5(transpiled);
3202  
  /* pcall 2 */ } catch (Throwable __e) { printStackTrace(__e); }
3203  
  String transpiledSrc = getServerTranspiled(formatSnippetID(id), md5);
3204  
  if (eq(transpiledSrc, "SAME")) {
3205  
    print("SAME");
3206  
    transpiledSrc = loadCachedTranspilation(formatSnippetID(id));
3207  
  }
3208  
  return transpiledSrc;
3209  
}
3210  
3211  
static String loadCachedTranspilation(String id) {
3212  
  return loadTextFile(new File(getCodeProgramDir(id), "Transpilation"));
3213  
}
3214  
3215  
static Object call(Object o) {
3216  
    return callFunction(o);
3217  
  }
3218  
  
3219  
  // varargs assignment fixer for a single string array argument
3220  
  static Object call(Object o, String method, String[] arg) {
3221  
    return call(o, method, new Object[] {arg});
3222  
  }
3223  
  
3224  
  static Object call(Object o, String method, Object... args) {
3225  
    try {
3226  
      if (o instanceof Class) {
3227  
        Method m = call_findStaticMethod((Class) o, method, args, false);
3228  
        m.setAccessible(true);
3229  
        return m.invoke(null, args);
3230  
      } else {
3231  
        Method m = call_findMethod(o, method, args, false);
3232  
        m.setAccessible(true);
3233  
        return m.invoke(o, args);
3234  
      }
3235  
    } catch (Exception e) {
3236  
      throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
3237  
    }
3238  
  }
3239  
3240  
  static Method call_findStaticMethod(Class c, String method, Object[] args, boolean debug) {
3241  
    Class _c = c;
3242  
    while (c != null) {
3243  
      for (Method m : c.getDeclaredMethods()) {
3244  
        if (debug)
3245  
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
3246  
        if (!m.getName().equals(method)) {
3247  
          if (debug) System.out.println("Method name mismatch: " + method);
3248  
          continue;
3249  
        }
3250  
3251  
        if ((m.getModifiers() & Modifier.STATIC) == 0 || !call_checkArgs(m, args, debug))
3252  
          continue;
3253  
3254  
        return m;
3255  
      }
3256  
      c = c.getSuperclass();
3257  
    }
3258  
    throw new RuntimeException("Method '" + method + "' (static) with " + args.length + " parameter(s) not found in " + _c.getName());
3259  
  }
3260  
3261  
  static Method call_findMethod(Object o, String method, Object[] args, boolean debug) {
3262  
    Class c = o.getClass();
3263  
    while (c != null) {
3264  
      for (Method m : c.getDeclaredMethods()) {
3265  
        if (debug)
3266  
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
3267  
        if (m.getName().equals(method) && call_checkArgs(m, args, debug))
3268  
          return m;
3269  
      }
3270  
      c = c.getSuperclass();
3271  
    }
3272  
    throw new RuntimeException("Method '" + method + "' (non-static) with " + args.length + " parameter(s) not found in " + o.getClass().getName());
3273  
  }
3274  
3275  
  private static boolean call_checkArgs(Method m, Object[] args, boolean debug) {
3276  
    Class<?>[] types = m.getParameterTypes();
3277  
    if (types.length != args.length) {
3278  
      if (debug)
3279  
        System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
3280  
      return false;
3281  
    }
3282  
    for (int i = 0; i < types.length; i++)
3283  
      if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
3284  
        if (debug)
3285  
          System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
3286  
        return false;
3287  
      }
3288  
    return true;
3289  
  }
3290  
3291  
3292  
static Object first(Object list) {
3293  
  return ((List) list).isEmpty() ? null : ((List) list).get(0);
3294  
}
3295  
3296  
static <A> A first(List<A> list) {
3297  
  return list.isEmpty() ? null : list.get(0);
3298  
}
3299  
3300  
static <A> A first(A[] bla) {
3301  
  return bla == null || bla.length == 0 ? null : bla[0];
3302  
}
3303  
  static String format3(String pat, Object... args) {
3304  
    if (args.length == 0) return pat;
3305  
    
3306  
    List<String> tok = javaTokPlusPeriod(pat);
3307  
    int argidx = 0;
3308  
    for (int i = 1; i < tok.size(); i += 2)
3309  
      if (tok.get(i).equals("*"))
3310  
        tok.set(i, format3_formatArg(argidx < args.length ? args[argidx++] : "null"));
3311  
    return join(tok);
3312  
  }
3313  
  
3314  
  static String format3_formatArg(Object arg) {
3315  
    if (arg == null) return "null";
3316  
    if (arg instanceof String) {
3317  
      String s = (String) arg;
3318  
      return isIdentifier(s) || isNonNegativeInteger(s) ? s : quote(s);
3319  
    }
3320  
    if (arg instanceof Integer || arg instanceof Long) return String.valueOf(arg);
3321  
    return quote(structure(arg));
3322  
  }
3323  
  
3324  
3325  
  // class Matches is added by #752
3326  
 
3327  
  static boolean match3(String pat, String s) {
3328  
    return match3(pat, s, null);
3329  
  }
3330  
  
3331  
  static boolean match3(String pat, String s, Matches matches) {
3332  
    if (s == null) return false;
3333  
    return match3(pat, parse3(s), matches);
3334  
  }
3335  
    
3336  
  static boolean match3(String pat, List<String> toks, Matches matches) {
3337  
    List<String> tokpat = parse3(pat);
3338  
    return match3(tokpat,toks,matches);
3339  
  }
3340  
3341  
  static boolean match3(List<String> tokpat, List<String> toks, Matches matches) {
3342  
3343  
    String[] m = match2(tokpat, toks);
3344  
    //print(structure(tokpat) + " on " + structure(toks) + " => " + structure(m));
3345  
    if (m == null)
3346  
      return false;
3347  
    else {
3348  
      if (matches != null) matches.m = m;
3349  
      return true;
3350  
    }
3351  
  }
3352  
static class Android3 {
3353  
  String greeting;
3354  
  boolean publicOverride; // optionally set this in client
3355  
  int startPort = 5000; // optionally set this in client
3356  
  Responder responder;
3357  
  boolean console = true;
3358  
  boolean daemon = false;
3359  
  boolean incomingSilent = false;
3360  
  boolean useMultiPort = true;
3361  
  boolean verbose;
3362  
  
3363  
  // set by system
3364  
  int port;
3365  
  long vport;
3366  
  DialogHandler handler;
3367  
  ServerSocket server;
3368  
  
3369  
  Android3(String greeting) {
3370  
  this.greeting = greeting;}
3371  
  Android3() {}
3372  
  
3373  
  synchronized void dispose() {
3374  
    if (server != null) {
3375  
      try {
3376  
        server.close();
3377  
      } catch (IOException e) {
3378  
        print("[internal] " + e);
3379  
      }
3380  
      server = null;
3381  
    }
3382  
    if (vport != 0) try {
3383  
      //print("Dispoing virtual port " + vport);
3384  
      removeFromMultiPort(vport);
3385  
      vport = 0;
3386  
    } catch (Throwable __e) { printStackTrace(__e); }
3387  
  }
3388  
}
3389  
3390  
static abstract class Responder {
3391  
  abstract String answer(String s, List<String> history);
3392  
}
3393  
3394  
static Android3 makeAndroid3(final String greeting) {
3395  
  return makeAndroid3(new Android3(greeting));
3396  
}
3397  
3398  
static Android3 makeAndroid3(final String greeting, Responder responder) {
3399  
  Android3 android = new Android3(greeting);
3400  
  android.responder = responder;
3401  
  return makeAndroid3(android);
3402  
}
3403  
3404  
static Android3 makeAndroid3(final Android3 a) {
3405  
  if (a.responder == null)
3406  
    a.responder = new Responder() {
3407  
      String answer(String s, List<String> history) {
3408  
        return callStaticAnswerMethod(s, history);
3409  
      }
3410  
    };
3411  
    
3412  
  print(a.greeting);
3413  
  
3414  
  if (a.useMultiPort) {
3415  
    a.vport = addToMultiPort(a.greeting,
3416  
      makeAndroid3_verboseResponder(a));
3417  
    if (a.vport == 1)
3418  
      makeAndroid3_handleConsole(a);
3419  
    return a;
3420  
  }
3421  
    
3422  
  a.handler = makeAndroid3_makeDialogHandler(a);
3423  
  a.port = a.daemon
3424  
    ? startDialogServerOnPortAboveDaemon(a.startPort, a.handler)
3425  
    : startDialogServerOnPortAbove(a.startPort, a.handler);
3426  
  a.server = startDialogServer_serverSocket;
3427  
3428  
  if (a.console && makeAndroid3_consoleInUse()) a.console = false;
3429  
  
3430  
  if (a.console)
3431  
    makeAndroid3_handleConsole(a);
3432  
3433  
  record(a);
3434  
  return a;
3435  
}
3436  
3437  
static void makeAndroid3_handleConsole(final Android3 a) {
3438  
  // Console handling stuff
3439  
  print("You may also type on this console.");
3440  
  { Thread _t_0 = new Thread() {
3441  
public void run() {
3442  
try {
3443  
    List<String> history = new ArrayList<String>();
3444  
    String line;
3445  
    while ((line = readLine()) != null) {
3446  
      /*if (eq(line, "bye")) {
3447  
        print("> bye stranger");
3448  
        history = new ArrayList<S>();
3449  
      } else*/ {
3450  
        history.add(line);
3451  
        history.add(makeAndroid3_getAnswer(line, history, a)); // prints answer on console too
3452  
      }
3453  
    }
3454  
  } catch (Exception _e) {
3455  
  throw _e instanceof RuntimeException ? (RuntimeException) _e : new RuntimeException(_e); } }
3456  
};
3457  
_t_0.start(); }
3458  
}
3459  
3460  
static DialogHandler makeAndroid3_makeDialogHandler(final Android3 a) {
3461  
  return new DialogHandler() {
3462  
public void run(final DialogIO io) {
3463  
    if (!a.publicOverride && !(publicCommOn() || io.isLocalConnection())) {
3464  
      io.sendLine("Sorry, not allowed");
3465  
      return;
3466  
    }
3467  
    
3468  
    String dialogID = randomID(8);
3469  
    
3470  
    io.sendLine(a.greeting + " / Your ID: " + dialogID);
3471  
    
3472  
    List<String> history = new ArrayList<String>();
3473  
    
3474  
    while (io.isStillConnected()) {
3475  
      if (io.waitForLine()) {
3476  
        final String line = io.readLineNoBlock();
3477  
        String s = dialogID + " at " + now() + ": " + quote(line);
3478  
        if (!a.incomingSilent)
3479  
          print(s);
3480  
        if (line == "bye") {
3481  
          io.sendLine("bye stranger");
3482  
          return;
3483  
        }
3484  
        Matches m = new Matches();
3485  
        history.add(line);
3486  
        String answer;
3487  
        if (match3("this is a continuation of talk *", s, m)
3488  
          || match3("hello bot! this is a continuation of talk *", s, m)) {
3489  
          dialogID = unquote(m.m[0]);
3490  
          answer = "ok";
3491  
        } else
3492  
          answer = makeAndroid3_getAnswer(line, history, a);
3493  
        history.add(answer);
3494  
        io.sendLine(answer);
3495  
        //appendToLog(logFile, s);
3496  
      }
3497  
    }
3498  
  }};
3499  
}
3500  
3501  
static String makeAndroid3_getAnswer(String line, List<String> history, Android3 a) {
3502  
  String answer;
3503  
  try {
3504  
    answer = makeAndroid3_fallback(line, history, a.responder.answer(line, history));
3505  
  } catch (Throwable e) {
3506  
    e = getInnerException(e);
3507  
    printStackTrace(e);
3508  
    answer = e.toString();
3509  
  }
3510  
  if (!a.incomingSilent)
3511  
    print("> " + shorten(answer, 500));
3512  
  return answer;
3513  
}
3514  
3515  
static String makeAndroid3_fallback(String s, List<String> history, String answer) {
3516  
  // Now we only do the safe thing instead of VM inspection - give out our process ID
3517  
  if (answer == null && match3("what is your pid", s))
3518  
    return getPID();
3519  
    
3520  
  if (answer == null && match3("what is your program id", s)) // should be fairly safe, right?
3521  
    return getProgramID();
3522  
    
3523  
  if (match3("get injection id", s))
3524  
    return getInjectionID();
3525  
    
3526  
  if (answer == null) answer = "?";
3527  
  if (answer.indexOf('\n') >= 0 || answer.indexOf('\r') >= 0)
3528  
    answer = quote(answer);
3529  
  return answer;
3530  
}
3531  
3532  
static boolean makeAndroid3_consoleInUse() {
3533  
  for (Object o : record_list)
3534  
    if (o instanceof Android3 && ((Android3) o).console)
3535  
      return true;
3536  
  return false;
3537  
}
3538  
3539  
static Responder makeAndroid3_verboseResponder(final Android3 a) {
3540  
  return new Responder() {
3541  
    String answer(String s, List<String> history) {
3542  
      if (a.verbose)
3543  
        print("> " + s);
3544  
      String answer = a.responder.answer(s, history);
3545  
      if (a.verbose)
3546  
        print("< " + answer);
3547  
      return answer;
3548  
    }
3549  
  };
3550  
}
3551  
static void callMain(Object c, String... args) {
3552  
  callOpt(c, "main", new Object[] {args});
3553  
}
3554  
static boolean empty(Collection c) {
3555  
  return isEmpty(c);
3556  
}
3557  
3558  
static boolean empty(String s) {
3559  
  return isEmpty(s);
3560  
}
3561  
3562  
static boolean empty(Map map) {
3563  
  return map == null || map.isEmpty();
3564  
}
3565  
3566  
static boolean empty(Object o) {
3567  
  if (o instanceof Collection) return empty((Collection) o);
3568  
  if (o instanceof String) return empty((String) o);
3569  
  if (o instanceof Map) return empty((Map) o);
3570  
  return false;
3571  
}
3572  
static String getServerTranspiled(String snippetID) {
3573  
  return getServerTranspiled(snippetID, null);
3574  
}
3575  
3576  
// returns "SAME" if md5 matches
3577  
static String getServerTranspiled(String snippetID, String expectedMD5) { try {
3578  
 
3579  
  long id = parseSnippetID(snippetID);
3580  
  /*S t = getTranspilationFromBossBot(id);
3581  
  if (t != null) return t;*/
3582  
  
3583  
  String text = loadPage_utf8(tb_mainServer() + "/tb-int/get-transpiled.php?raw=1&withlibs=1&id=" + id + "&utf8=1"
3584  
    + (l(expectedMD5) > 1 ? "&md5=" + urlencode(expectedMD5) : "")
3585  
    + standardCredentials());
3586  
  if (nempty(text) && neq(text, "SAME"))
3587  
    saveTranspiledCode(snippetID, text);
3588  
  return text;
3589  
3590  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
3591  
3592  
 /** writes safely (to temp file, then rename) */
3593  
  public static void saveTextFile(String fileName, String contents) throws IOException {
3594  
    File file = new File(fileName);
3595  
    File parentFile = file.getParentFile();
3596  
    if (parentFile != null)
3597  
      parentFile.mkdirs();
3598  
    String tempFileName = fileName + "_temp";
3599  
    File tempFile = new File(tempFileName);
3600  
    if (contents != null) {
3601  
      if (tempFile.exists()) try {
3602  
        String saveName = tempFileName + ".saved." + now();
3603  
        copyFile(tempFile, new File(saveName));
3604  
      } catch (Throwable e) { printStackTrace(e); }
3605  
      FileOutputStream fileOutputStream = new FileOutputStream(tempFile.getPath());
3606  
      OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");
3607  
      PrintWriter printWriter = new PrintWriter(outputStreamWriter);
3608  
      printWriter.print(contents);
3609  
      printWriter.close();
3610  
    }
3611  
    
3612  
    if (file.exists() && !file.delete())
3613  
      throw new IOException("Can't delete " + fileName);
3614  
3615  
    if (contents != null)
3616  
      if (!tempFile.renameTo(file))
3617  
        throw new IOException("Can't rename " + tempFile + " to " + file);
3618  
  }
3619  
  
3620  
  public static void saveTextFile(File fileName, String contents) {
3621  
    try {
3622  
      saveTextFile(fileName.getPath(), contents);
3623  
    } catch (IOException e) {
3624  
      throw new RuntimeException(e);
3625  
    }
3626  
  }
3627  
  
3628  
  static String getStackTrace(Throwable throwable) {
3629  
  StringWriter writer = new StringWriter();
3630  
  throwable.printStackTrace(new PrintWriter(writer));
3631  
  return writer.toString();
3632  
}
3633  
static String formatSnippetID(String id) {
3634  
  return "#" + parseSnippetID(id);
3635  
}
3636  
3637  
static String formatSnippetID(long id) {
3638  
  return "#" + id;
3639  
}
3640  
// extended over Class.isInstance() to handle primitive types
3641  
static boolean isInstanceX(Class type, Object arg) {
3642  
  if (type == boolean.class) return arg instanceof Boolean;
3643  
  if (type == int.class) return arg instanceof Integer;
3644  
  if (type == long.class) return arg instanceof Long;
3645  
  if (type == float.class) return arg instanceof Float;
3646  
  if (type == short.class) return arg instanceof Short;
3647  
  if (type == char.class) return arg instanceof Character;
3648  
  if (type == byte.class) return arg instanceof Byte;
3649  
  if (type == double.class) return arg instanceof Double;
3650  
  return type.isInstance(arg);
3651  
}
3652  
static boolean eq(Object a, Object b) {
3653  
  if (a == null) return b == null;
3654  
  if (a.equals(b)) return true;
3655  
  if (a instanceof BigInteger) {
3656  
    if (b instanceof Integer) return a.equals(BigInteger.valueOf((Integer) b));
3657  
    if (b instanceof Long) return a.equals(BigInteger.valueOf((Long) b));
3658  
  }
3659  
  return false;
3660  
}
3661  
 static String getSnippetFromBossBot(long snippetID) {
3662  
    return boss(format3("get text for *", snippetID));
3663  
  }
3664  
  
3665  
static <A, B> Set<A> keys(Map<A, B> map) {
3666  
  return map.keySet();
3667  
}
3668  
3669  
static Set keys(Object map) {
3670  
  return keys((Map) map);
3671  
}
3672  
static List emptyList() {
3673  
  return new ArrayList();
3674  
  //ret Collections.emptyList();
3675  
}
3676  
// try to get our current process ID
3677  
static String getPID() {
3678  
  String name = ManagementFactory.getRuntimeMXBean().getName();
3679  
  return name.replaceAll("@.*", "");
3680  
}
3681  
  public static String unquote(String s) {
3682  
    if (s.startsWith("[")) {
3683  
      int i = 1;
3684  
      while (i < s.length() && s.charAt(i) == '=') ++i;
3685  
      if (i < s.length() && s.charAt(i) == '[') {
3686  
        String m = s.substring(1, i);
3687  
        if (s.endsWith("]" + m + "]"))
3688  
          return s.substring(i+1, s.length()-i-1);
3689  
      }
3690  
    }
3691  
    
3692  
    if (s.startsWith("\"") /*&& s.endsWith("\"")*/ && s.length() > 1) {
3693  
      String st = s.substring(1, s.endsWith("\"") ? s.length()-1 : s.length());
3694  
      StringBuilder sb = new StringBuilder(st.length());
3695  
  
3696  
      for (int i = 0; i < st.length(); i++) {
3697  
        char ch = st.charAt(i);
3698  
        if (ch == '\\') {
3699  
          char nextChar = (i == st.length() - 1) ? '\\' : st
3700  
                  .charAt(i + 1);
3701  
          // Octal escape?
3702  
          if (nextChar >= '0' && nextChar <= '7') {
3703  
              String code = "" + nextChar;
3704  
              i++;
3705  
              if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
3706  
                      && st.charAt(i + 1) <= '7') {
3707  
                  code += st.charAt(i + 1);
3708  
                  i++;
3709  
                  if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
3710  
                          && st.charAt(i + 1) <= '7') {
3711  
                      code += st.charAt(i + 1);
3712  
                      i++;
3713  
                  }
3714  
              }
3715  
              sb.append((char) Integer.parseInt(code, 8));
3716  
              continue;
3717  
          }
3718  
          switch (nextChar) {
3719  
          case '\\':
3720  
              ch = '\\';
3721  
              break;
3722  
          case 'b':
3723  
              ch = '\b';
3724  
              break;
3725  
          case 'f':
3726  
              ch = '\f';
3727  
              break;
3728  
          case 'n':
3729  
              ch = '\n';
3730  
              break;
3731  
          case 'r':
3732  
              ch = '\r';
3733  
              break;
3734  
          case 't':
3735  
              ch = '\t';
3736  
              break;
3737  
          case '\"':
3738  
              ch = '\"';
3739  
              break;
3740  
          case '\'':
3741  
              ch = '\'';
3742  
              break;
3743  
          // Hex Unicode: u????
3744  
          case 'u':
3745  
              if (i >= st.length() - 5) {
3746  
                  ch = 'u';
3747  
                  break;
3748  
              }
3749  
              int code = Integer.parseInt(
3750  
                      "" + st.charAt(i + 2) + st.charAt(i + 3)
3751  
                              + st.charAt(i + 4) + st.charAt(i + 5), 16);
3752  
              sb.append(Character.toChars(code));
3753  
              i += 5;
3754  
              continue;
3755  
          default:
3756  
            ch = nextChar; // added by Stefan
3757  
          }
3758  
          i++;
3759  
        }
3760  
        sb.append(ch);
3761  
      }
3762  
      return sb.toString();      
3763  
    } else
3764  
      return s; // return original
3765  
  }
3766  
  // compile JavaX source, load classes & return main class
3767  
  // src can be a snippet ID or actual source code
3768  
  // TODO: record injection?
3769  
  
3770  
  static Class<?> hotwire(String src) {
3771  
    try {
3772  
      Class j = getJavaX();
3773  
      
3774  
      synchronized(j) { // hopefully this goes well...
3775  
        List<File> libraries = new ArrayList<File>();
3776  
        File srcDir = (File) call(j, "transpileMain", src, libraries);
3777  
        if (srcDir == null)
3778  
          throw fail("transpileMain returned null (src=" + quote(src) + ")");
3779  
        
3780  
        Object androidContext = get(j, "androidContext");
3781  
        if (androidContext != null)
3782  
          return (Class) call(j, "loadx2android", srcDir, src);
3783  
          
3784  
        File classesDir = (File) call(j, "TempDirMaker_make");
3785  
        String javacOutput = (String) call(j, "compileJava", srcDir, libraries, classesDir);
3786  
        System.out.println(javacOutput);
3787  
        
3788  
        URL[] urls = new URL[libraries.size()+1];
3789  
        urls[0] = classesDir.toURI().toURL();
3790  
        for (int i = 0; i < libraries.size(); i++)
3791  
          urls[i+1] = libraries.get(i).toURI().toURL();
3792  
  
3793  
        // make class loader
3794  
        URLClassLoader classLoader = new URLClassLoader(urls);
3795  
    
3796  
        // load & return main class
3797  
        Class<?> theClass = classLoader.loadClass("main");
3798  
        
3799  
        callOpt(j, "registerSourceCode", theClass, loadTextFile(new File(srcDir, "main.java")));
3800  
3801  
        call(j, "setVars", theClass, isSnippetID(src) ? src: null);
3802  
        
3803  
        if (isSnippetID(src))
3804  
          callOpt(j, "addInstance", src, theClass);
3805  
          
3806  
        if (!_inCore())
3807  
          hotwire_copyOver(theClass);
3808  
  
3809  
        return theClass;
3810  
      }
3811  
    } catch (Exception e) {
3812  
      throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
3813  
    }
3814  
  }
3815  
static void smartSet(Field f, Object o, Object value) throws Exception {
3816  
  f.setAccessible(true);
3817  
  
3818  
  // take care of common case (long to int)
3819  
  if (f.getType() == int.class && value instanceof Long)
3820  
    value = ((Long) value).intValue();
3821  
    
3822  
  f.set(o, value);
3823  
}
3824  
static String structure(Object o) {
3825  
  HashSet refd = new HashSet();
3826  
  return structure_2(structure_1(o, 0, new IdentityHashMap(), refd), refd);
3827  
}
3828  
3829  
// leave to false, unless unstructure() breaks
3830  
static boolean structure_allowShortening = false;
3831  
3832  
static String structure_1(Object o, int stringSizeLimit, IdentityHashMap<Object, Integer> seen, HashSet<Integer> refd) {
3833  
  if (o == null) return "null";
3834  
  
3835  
  // these are never back-referenced (for readability)
3836  
  
3837  
  if (o instanceof String)
3838  
    return quote(stringSizeLimit != 0 ? shorten((String) o, stringSizeLimit) : (String) o);
3839  
    
3840  
  if (o instanceof BigInteger)
3841  
    return "bigint(" + o + ")";
3842  
  
3843  
  if (o instanceof Double)
3844  
    return "d(" + quote(str(o)) + ")";
3845  
    
3846  
  if (o instanceof Long)
3847  
    return o + "L";
3848  
  
3849  
  if (o instanceof Integer)
3850  
    return str(o);
3851  
    
3852  
  if (o instanceof Boolean)
3853  
    return ((Boolean) o).booleanValue() ? "t" : "f";
3854  
    
3855  
  if (o instanceof Character)
3856  
    return quoteCharacter((Character) o);
3857  
    
3858  
  if (o instanceof File)
3859  
    return "File " + quote(((File) o).getPath());
3860  
    
3861  
  // referencable objects follow
3862  
  
3863  
  Integer ref = seen.get(o);
3864  
  if (ref != null) {
3865  
    refd.add(ref);
3866  
    return "r" + ref;
3867  
  }
3868  
      
3869  
  ref = seen.size()+1;
3870  
  seen.put(o, ref);
3871  
  String r = "m" + ref + " "; // marker
3872  
3873  
  String name = o.getClass().getName();
3874  
3875  
  StringBuilder buf = new StringBuilder();
3876  
  
3877  
  if (o instanceof HashSet)
3878  
    return r + "hashset " + structure_1(new ArrayList((Set) o), stringSizeLimit, seen, refd);
3879  
3880  
  if (o instanceof TreeSet)
3881  
    return r + "treeset " + structure_1(new ArrayList((Set) o), stringSizeLimit, seen, refd);
3882  
  
3883  
  if (o instanceof Collection) {
3884  
    for (Object x : (Collection) o) {
3885  
      if (buf.length() != 0) buf.append(", ");
3886  
      buf.append(structure_1(x, stringSizeLimit, seen, refd));
3887  
    }
3888  
    return r + "[" + buf + "]";
3889  
  }
3890  
  
3891  
  if (o instanceof Map) {
3892  
    for (Object e : ((Map) o).entrySet()) {
3893  
      if (buf.length() != 0) buf.append(", ");
3894  
      buf.append(structure_1(((Map.Entry) e).getKey(), stringSizeLimit, seen, refd));
3895  
      buf.append("=");
3896  
      buf.append(structure_1(((Map.Entry) e).getValue(), stringSizeLimit, seen, refd));
3897  
    }
3898  
    return r + (o instanceof HashMap ? "hashmap" : "") + "{" + buf + "}";
3899  
  }
3900  
  
3901  
  if (o.getClass().isArray()) {
3902  
    int n = Array.getLength(o);
3903  
    for (int i = 0; i < n; i++) {
3904  
      if (buf.length() != 0) buf.append(", ");
3905  
      buf.append(structure_1(Array.get(o, i), stringSizeLimit, seen, refd));
3906  
    }
3907  
    return r + "array{" + buf + "}";
3908  
  }
3909  
3910  
  if (o instanceof Class)
3911  
    return r + "class(" + quote(((Class) o).getName()) + ")";
3912  
    
3913  
  if (o instanceof Throwable)
3914  
    return r + "exception(" + quote(((Throwable) o).getMessage()) + ")";
3915  
    
3916  
  if (o instanceof BitSet) {
3917  
    BitSet bs = (BitSet) o;
3918  
    for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) {
3919  
      if (buf.length() != 0) buf.append(", ");
3920  
       buf.append(i);
3921  
    }
3922  
    return "bitset{" + buf + "}";
3923  
  }
3924  
    
3925  
  // Need more cases? This should cover all library classes...
3926  
  if (name.startsWith("java.") || name.startsWith("javax."))
3927  
    return r + String.valueOf(o);
3928  
    
3929  
  String shortName = o.getClass().getName().replaceAll("^main\\$", "");
3930  
  
3931  
  if (shortName.equals("Lisp")) {
3932  
    buf.append("l(" + structure_1(getOpt(o, "head"), stringSizeLimit, seen, refd));
3933  
    List args = (List) ( getOpt(o, "args"));
3934  
    if (nempty(args))
3935  
      for (int i = 0; i < l(args); i++) {
3936  
        buf.append(", ");
3937  
        Object arg = args.get(i);
3938  
        
3939  
        // sweet shortening
3940  
        if (arg != null && eq(arg.getClass().getName(), "main$Lisp") && isTrue(call(arg, "isEmpty")))
3941  
          arg = get(arg, "head");
3942  
          
3943  
        buf.append(structure_1(arg, stringSizeLimit, seen, refd));
3944  
      }
3945  
    buf.append(")");
3946  
    return r + str(buf);
3947  
  }
3948  
    
3949  
  int numFields = 0;
3950  
  String fieldName = "";
3951  
  if (shortName.equals("DynamicObject")) {
3952  
    shortName = (String) get(o, "className");
3953  
    Map<String, Object> fieldValues = (Map) get(o, "fieldValues");
3954  
    
3955  
    for (String _fieldName : fieldValues.keySet()) {
3956  
      fieldName = _fieldName;
3957  
      Object value = fieldValues.get(fieldName);
3958  
      if (value != null) {
3959  
        if (buf.length() != 0) buf.append(", ");
3960  
        buf.append(fieldName + "=" + structure_1(value, stringSizeLimit, seen, refd));
3961  
      }
3962  
      ++numFields;
3963  
   }
3964  
  } else {
3965  
    // regular class
3966  
    
3967  
    Class c = o.getClass();
3968  
    while (c != Object.class) {
3969  
      Field[] fields = c.getDeclaredFields();
3970  
      for (Field field : fields) {
3971  
        if ((field.getModifiers() & Modifier.STATIC) != 0)
3972  
          continue;
3973  
        fieldName = field.getName();
3974  
        
3975  
        // skip outer object reference
3976  
        if (fieldName.indexOf("$") >= 0) continue;
3977  
  
3978  
        Object value;
3979  
        try {
3980  
          field.setAccessible(true);
3981  
          value = field.get(o);
3982  
        } catch (Exception e) {
3983  
          value = "?";
3984  
        }
3985  
        
3986  
        // put special cases here...
3987  
    
3988  
        if (value != null) {
3989  
          if (buf.length() != 0) buf.append(", ");
3990  
          buf.append(fieldName + "=" + structure_1(value, stringSizeLimit, seen, refd));
3991  
        }
3992  
        ++numFields;
3993  
      }
3994  
      c = c.getSuperclass();
3995  
    }
3996  
  }
3997  
  
3998  
  String b = buf.toString();
3999  
  
4000  
  if (numFields == 1 && structure_allowShortening)
4001  
    b = b.replaceAll("^" + fieldName + "=", ""); // drop field name if only one
4002  
  String s = shortName;
4003  
  if (buf.length() != 0)
4004  
    s += "(" + b + ")";
4005  
  return r + s;
4006  
}
4007  
4008  
// drop unused markers
4009  
static String structure_2(String s, HashSet<Integer> refd) {
4010  
  List<String> tok = javaTok(s);
4011  
  StringBuilder out = new StringBuilder();
4012  
  for (int i = 1; i < l(tok); i += 2) {
4013  
    String t = tok.get(i);
4014  
    if (t.startsWith("m") && isInteger(t.substring(1))
4015  
      && !refd.contains(parseInt(t.substring(1))))
4016  
      continue;
4017  
    out.append(t).append(tok.get(i+1));
4018  
  }
4019  
  return str(out);
4020  
}
4021  
4022  
static boolean master() {
4023  
  return webAuthed() || litlist("stefanreich", "bgrgndz", "okhan", "mrshutco").contains(getUserName());
4024  
}
4025  
static int parseInt(String s) {
4026  
  return empty(s) ? 0 : Integer.parseInt(s);
4027  
}
4028  
4029  
static String randomID(int length) {
4030  
  return makeRandomID(length);
4031  
}
4032  
static String makeResponder_callAnswerMethod(Object bot, String s, List<String> history) {
4033  
  String answer = (String) callOpt(bot, "answer", s, history);
4034  
  if (answer == null)
4035  
    answer = (String) callOpt(bot, "answer", s);
4036  
  return answer;
4037  
}
4038  
4039  
static Responder makeResponder(final Object bot) {
4040  
  if (bot instanceof Responder) return (Responder) bot;
4041  
  
4042  
  return new Responder() {
4043  
    String answer(String s, List<String> history) {
4044  
      return makeResponder_callAnswerMethod(bot, s, history);
4045  
    }
4046  
  };
4047  
}
4048  
static Object getOpt(Object o, String field) {
4049  
  if (o instanceof String) o = getBot ((String) o);
4050  
  if (o == null) return null;
4051  
  if (o instanceof Class) return getOpt((Class) o, field);
4052  
  
4053  
  if (o.getClass().getName().equals("main$DynamicObject"))
4054  
    return ((Map) getOpt_raw(o, "fieldValues")).get(field);
4055  
  
4056  
  if (o instanceof Map) return ((Map) o).get(field);
4057  
  
4058  
  return getOpt_raw(o, field);
4059  
}
4060  
4061  
static Object getOpt_raw(Object o, String field) {
4062  
  try {
4063  
    Field f = getOpt_findField(o.getClass(), field);
4064  
    if (f == null) return null;
4065  
    f.setAccessible(true);
4066  
    return f.get(o);
4067  
  } catch (Exception e) {
4068  
    throw new RuntimeException(e);
4069  
  }
4070  
}
4071  
4072  
static Object getOpt(Class c, String field) {
4073  
  try {
4074  
    Field f = getOpt_findStaticField(c, field);
4075  
    if (f == null) return null;
4076  
    f.setAccessible(true);
4077  
    return f.get(null);
4078  
  } catch (Exception e) {
4079  
    throw new RuntimeException(e);
4080  
  }
4081  
}
4082  
4083  
static Field getOpt_findStaticField(Class<?> c, String field) {
4084  
  Class _c = c;
4085  
  do {
4086  
    for (Field f : _c.getDeclaredFields())
4087  
      if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0)
4088  
        return f;
4089  
    _c = _c.getSuperclass();
4090  
  } while (_c != null);
4091  
  return null;
4092  
}
4093  
4094  
static Field getOpt_findField(Class<?> c, String field) {
4095  
  Class _c = c;
4096  
  do {
4097  
    for (Field f : _c.getDeclaredFields())
4098  
      if (f.getName().equals(field))
4099  
        return f;
4100  
    _c = _c.getSuperclass();
4101  
  } while (_c != null);
4102  
  return null;
4103  
}
4104  
4105  
static Object callF(Object f, Object... args) {
4106  
  return callFunction(f, args);
4107  
}
4108  
4109  
static Object callFunction(Object f, Object... args) {
4110  
  if (f == null) return null;
4111  
  if (f instanceof Runnable) {
4112  
    ((Runnable) f).run();
4113  
    return null;
4114  
  } else if (f instanceof String)
4115  
    return call(mc(), (String) f, args);
4116  
  else
4117  
    return call(f, "get", args);
4118  
  //else throw fail("Can't call a " + getClassName(f));
4119  
}
4120  
static String shorten(String s, int max) {
4121  
  if (s == null) return "";
4122  
  return s.length() <= max ? s : s.substring(0, Math.min(s.length(), max)) + "...";
4123  
}
4124  
static List<Object> record_list = synchroList();
4125  
4126  
static void record(Object o) {
4127  
  record_list.add(o);
4128  
}
4129  
static Object getBot(String botID) {
4130  
  return callOpt(getMainBot(), "getBot", botID);
4131  
}
4132  
4133  
static String quoteCharacter(char c) {
4134  
  if (c == '\'') return "'\\''";
4135  
  if (c == '\\') return "'\\\\'";
4136  
  return "'" + c + "'";
4137  
}
4138  
4139  
static String loadPage_utf8(String url) {
4140  
  loadPage_charset.set("UTF-8");
4141  
  try {
4142  
    return loadPage(url);
4143  
  } finally {
4144  
    loadPage_charset.set(null);
4145  
  }
4146  
}
4147  
static void printStackTrace(Throwable e) {
4148  
  // we go to system.out now - system.err is nonsense
4149  
  print(getStackTrace(e));
4150  
}
4151  
4152  
static void printStackTrace() {
4153  
  printStackTrace(new Throwable());
4154  
}
4155  
// currently finds only inner classes of class "main"
4156  
// returns null on not found
4157  
// this is the simple version that is not case-tolerant
4158  
static Class findClass(String name) {
4159  
  try {
4160  
    return Class.forName("main$" + name);
4161  
  } catch (ClassNotFoundException e) {
4162  
    return null;
4163  
  }
4164  
}
4165  
static void removeFromMultiPort(long vport) {
4166  
  for (Object port : getMultiPorts())
4167  
    call(port, "removePort", vport);
4168  
}
4169  
static String str(Object o) {
4170  
  return String.valueOf(o);
4171  
}
4172  
static boolean isJavaIdentifier(String s) {
4173  
  if (s.length() == 0 || !Character.isJavaIdentifierStart(s.charAt(0)))
4174  
    return false;
4175  
  for (int i = 1; i < s.length(); i++)
4176  
    if (!Character.isJavaIdentifierPart(s.charAt(i)))
4177  
      return false;
4178  
  return true;
4179  
}
4180  
static Object addToMultiPort_responder;
4181  
4182  
static long addToMultiPort(final String botName) {
4183  
  return addToMultiPort(botName, new Object() {
4184  
    public String answer(String s, List<String> history) {
4185  
      String answer = (String) ( callOpt(getMainClass(), "answer", s, history));
4186  
      if (answer != null) return answer;
4187  
      answer = (String) callOpt(getMainClass(), "answer", s);
4188  
      if (answer != null) return answer;
4189  
      if (match3("get injection id", s))
4190  
        return getInjectionID();
4191  
      return null;
4192  
    }
4193  
  });
4194  
}
4195  
4196  
static long addToMultiPort(final String botName, final Object responder) {
4197  
  print(botName);
4198  
  addToMultiPort_responder = responder;
4199  
  startMultiPort();
4200  
  List ports = getMultiPorts();
4201  
  if (ports == null) return 0;
4202  
  if (ports.isEmpty())
4203  
    throw fail("No multiports!");
4204  
  if (ports.size() > 1)
4205  
    print("Multiple multi-ports. Using last one.");
4206  
  Object port = last(ports);
4207  
  Object responder2 = new Object() {
4208  
    public String answer(String s, List<String> history) {
4209  
      if (match3("get injection id", s))
4210  
        return getInjectionID();
4211  
      if (match3("your name", s))
4212  
        return botName;
4213  
      return (String) call(responder, "answer", s, history);
4214  
    }
4215  
  };
4216  
  record(responder2);
4217  
  return (Long) call(port, "addResponder", botName, responder2);
4218  
}
4219  
4220  
static String programID;
4221  
4222  
static String getProgramID() {
4223  
  return nempty(programID) ? formatSnippetID(programID) : "?";
4224  
}
4225  
4226  
// TODO: ask JavaX instead
4227  
static String getProgramID(Class c) {
4228  
  String id = (String) getOpt(c, "programID");
4229  
  if (nempty(id))
4230  
    return formatSnippetID(id);
4231  
  return "?";
4232  
}
4233  
4234  
static String getProgramID(Object o) {
4235  
  return getProgramID(getMainClass(o));
4236  
}
4237  
4238  
  static String unnull(String s) {
4239  
    return s == null ? "" : s;
4240  
  }
4241  
  
4242  
  static <A> List<A> unnull(List<A> l) {
4243  
    return l == null ? emptyList() : l;
4244  
  }
4245  
  
4246  
  static Object[] unnull(Object[] a) {
4247  
    return a == null ? new Object[0] : a;
4248  
  }
4249  
// replacement for class JavaTok
4250  
// maybe incomplete, might want to add floating point numbers
4251  
// todo also: extended multi-line strings
4252  
4253  
static List<String> javaTok(String s) {
4254  
  List<String> tok = new ArrayList<String>();
4255  
  int l = s.length();
4256  
  
4257  
  int i = 0;
4258  
  while (i < l) {
4259  
    int j = i;
4260  
    char c; String cc;
4261  
    
4262  
    // scan for whitespace
4263  
    while (j < l) {
4264  
      c = s.charAt(j);
4265  
      cc = s.substring(j, Math.min(j+2, l));
4266  
      if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
4267  
        ++j;
4268  
      else if (cc.equals("/*")) {
4269  
        do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/"));
4270  
        j = Math.min(j+2, l);
4271  
      } else if (cc.equals("//")) {
4272  
        do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0);
4273  
      } else
4274  
        break;
4275  
    }
4276  
    
4277  
    tok.add(s.substring(i, j));
4278  
    i = j;
4279  
    if (i >= l) break;
4280  
    c = s.charAt(i); // cc is not needed in rest of loop body
4281  
    cc = s.substring(i, Math.min(i+2, l));
4282  
4283  
    // scan for non-whitespace
4284  
    if (c == '\'' || c == '"') {
4285  
      char opener = c;
4286  
      ++j;
4287  
      while (j < l) {
4288  
        if (s.charAt(j) == opener || s.charAt(j) == '\n') { // end at \n to not propagate unclosed string literal errors
4289  
          ++j;
4290  
          break;
4291  
        } else if (s.charAt(j) == '\\' && j+1 < l)
4292  
          j += 2;
4293  
        else
4294  
          ++j;
4295  
      }
4296  
    } else if (Character.isJavaIdentifierStart(c))
4297  
      do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || "'".indexOf(s.charAt(j)) >= 0)); // for stuff like "don't"
4298  
    else if (Character.isDigit(c)) {
4299  
      do ++j; while (j < l && Character.isDigit(s.charAt(j)));
4300  
      if (j < l && s.charAt(j) == 'L') ++j; // Long constants like 1L
4301  
    } else if (cc.equals("[[")) {
4302  
      do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]"));
4303  
      j = Math.min(j+2, l);
4304  
    } else if (cc.equals("[=") && i+2 < l && s.charAt(i+2) == '[') {
4305  
      do ++j; while (j+2 < l && !s.substring(j, j+3).equals("]=]"));
4306  
      j = Math.min(j+3, l);
4307  
    } else
4308  
      ++j;
4309  
4310  
    tok.add(s.substring(i, j));
4311  
    i = j;
4312  
  }
4313  
  
4314  
  if ((tok.size() % 2) == 0) tok.add("");
4315  
  return tok;
4316  
}
4317  
4318  
static List<String> javaTok(List<String> tok) {
4319  
  return javaTok(join(tok));
4320  
}
4321  
  static List<String> parse3(String s) {
4322  
    return dropPunctuation(javaTokPlusPeriod(s));
4323  
  }
4324  
static Object newObject(Class c, Object... args) {
4325  
  return nuObject(c, args);
4326  
}
4327  
4328  
static Object newObject(String className, Object... args) {
4329  
  return nuObject(className, args);
4330  
}
4331  
4332  
static boolean isLongConstant(String s) {
4333  
  if (!s.endsWith("L")) return false;
4334  
  s = s.substring(0, l(s)-1);
4335  
  return isInteger(s);
4336  
}
4337  
static Class __javax;
4338  
4339  
static Class getJavaX() {
4340  
  return __javax;
4341  
}
4342  
static <A> List<A> subList(List<A> l, int startIndex) {
4343  
  return subList(l, startIndex, l(l));
4344  
}
4345  
4346  
static <A> List<A> subList(List<A> l, int startIndex, int endIndex) {
4347  
  startIndex = max(0, min(l(l), startIndex));
4348  
  endIndex = max(0, min(l(l), endIndex));
4349  
  if (startIndex > endIndex) return litlist();
4350  
  return l.subList(startIndex, endIndex);
4351  
}
4352  
static Throwable getInnerException(Throwable e) {
4353  
  while (e.getCause() != null)
4354  
    e = e.getCause();
4355  
  return e;
4356  
}
4357  
static boolean isTrue(Object o) {
4358  
  return booleanValue(o);
4359  
}
4360  
static long parseLong(String s) {
4361  
  if (s == null) return 0;
4362  
  return Long.parseLong(dropSuffix("L", s));
4363  
}
4364  
4365  
static long parseLong(Object s) {
4366  
  return Long.parseLong((String) s);
4367  
}
4368  
static boolean isIdentifier(String s) {
4369  
  return isJavaIdentifier(s);
4370  
}
4371  
// match2 matches multiple "*" (matches a single token) wildcards and zero or one "..." wildcards (matches multiple tokens)
4372  
4373  
static String[] match2(List<String> pat, List<String> tok) {
4374  
  // standard case (no ...)
4375  
  int i = pat.indexOf("...");
4376  
  if (i < 0) return match2_match(pat, tok);
4377  
  
4378  
  pat = new ArrayList<String>(pat); // We're modifying it, so copy first
4379  
  pat.set(i, "*");
4380  
  while (pat.size() < tok.size()) {
4381  
    pat.add(i, "*");
4382  
    pat.add(i+1, ""); // doesn't matter
4383  
  }
4384  
  
4385  
  return match2_match(pat, tok);
4386  
}
4387  
4388  
static String[] match2_match(List<String> pat, List<String> tok) {
4389  
  List<String> result = new ArrayList<String>();
4390  
  if (pat.size() != tok.size()) {
4391  
    /*if (debug)
4392  
      print("Size mismatch: " + structure(pat) + " vs " + structure(tok));*/
4393  
    return null;
4394  
  }
4395  
  for (int i = 1; i < pat.size(); i += 2) {
4396  
    String p = pat.get(i), t = tok.get(i);
4397  
    /*if (debug)
4398  
      print("Checking " + p + " against " + t);*/
4399  
    if (eq(p, "*"))
4400  
      result.add(t);
4401  
    else if (!equalsIgnoreCase(unquote(p), unquote(t))) // bold change - match quoted and unquoted now
4402  
      return null;
4403  
  }
4404  
  return result.toArray(new String[result.size()]);
4405  
}
4406  
4407  
static void hotwire_copyOver(Class c) {
4408  
  synchronized(StringBuffer.class) {
4409  
    for (String field : litlist("print_log", "print_silent")) {
4410  
      Object o = get(mc(), field);
4411  
      if (o != null)
4412  
        setOpt(c, field, o);
4413  
    }
4414  
      
4415  
    Object mainBot = getMainBot();
4416  
    if (mainBot != null)
4417  
      setOpt(c, "mainBot", mainBot);
4418  
4419  
    setOpt(c, "creator_class", new WeakReference(mc()));
4420  
  }
4421  
}
4422  
static Class<?> _getClass(String name) {
4423  
  try {
4424  
    return Class.forName(name);
4425  
  } catch (ClassNotFoundException e) {
4426  
    return null;
4427  
  }
4428  
}
4429  
4430  
static Class _getClass(Object o) {
4431  
  return o instanceof Class ? (Class) o : o.getClass();
4432  
}
4433  
4434  
static Class _getClass(Object realm, String name) { try {
4435  
 
4436  
  return getClass(realm).getClassLoader().loadClass(classNameToVM(name));
4437  
4438  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
4439  
static boolean publicCommOn() {
4440  
  return "1".equals(loadTextFile(new File(userHome(), ".javax/public-communication")));
4441  
}
4442  
static String getUserName() {
4443  
  return (String) callOpt(getMainBot(), "getUserName");
4444  
}
4445  
static boolean isNonNegativeInteger(String s) {
4446  
  return s != null && Pattern.matches("\\d+", s);
4447  
}
4448  
static boolean hasMethod(Object o, String method, Object... args) {
4449  
  return findMethod(o, method, args) != null;
4450  
}
4451  
static Object nuObject(String className, Object... args) { try {
4452  
 
4453  
  return nuObject(Class.forName(className), args);
4454  
4455  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
4456  
4457  
static Object nuObject(Object realm, String className, Object... args) {
4458  
  return nuObject(_getClass(realm, className), args);
4459  
}
4460  
4461  
static <A> A nuObject(Class<A> c, Object... args) { try {
4462  
 
4463  
  Constructor m = nuObject_findConstructor(c, args);
4464  
  m.setAccessible(true);
4465  
  return (A) m.newInstance(args);
4466  
4467  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
4468  
4469  
static Constructor nuObject_findConstructor(Class c, Object... args) {
4470  
  for (Constructor m : c.getDeclaredConstructors()) {
4471  
    if (!nuObject_checkArgs(m.getParameterTypes(), args, false))
4472  
      continue;
4473  
    return m;
4474  
  }
4475  
  throw new RuntimeException("Constructor with " + args.length + " matching parameter(s) not found in " + c.getName());
4476  
}
4477  
4478  
 static boolean nuObject_checkArgs(Class[] types, Object[] args, boolean debug) {
4479  
    if (types.length != args.length) {
4480  
      if (debug)
4481  
        System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
4482  
      return false;
4483  
    }
4484  
    for (int i = 0; i < types.length; i++)
4485  
      if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
4486  
        if (debug)
4487  
          System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
4488  
        return false;
4489  
      }
4490  
    return true;
4491  
  }
4492  
// This is made for NL parsing.
4493  
// It's javaTok extended with "..." token, "$n" and "#n" and
4494  
// special quotes (which are converted to normal ones).
4495  
4496  
static List<String> javaTokPlusPeriod(String s) {
4497  
  List<String> tok = new ArrayList<String>();
4498  
  int l = s.length();
4499  
  
4500  
  int i = 0;
4501  
  while (i < l) {
4502  
    int j = i;
4503  
    char c; String cc;
4504  
    
4505  
    // scan for whitespace
4506  
    while (j < l) {
4507  
      c = s.charAt(j);
4508  
      cc = s.substring(j, Math.min(j+2, l));
4509  
      if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
4510  
        ++j;
4511  
      else if (cc.equals("/*")) {
4512  
        do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/"));
4513  
        j = Math.min(j+2, l);
4514  
      } else if (cc.equals("//")) {
4515  
        do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0);
4516  
      } else
4517  
        break;
4518  
    }
4519  
    
4520  
    tok.add(s.substring(i, j));
4521  
    i = j;
4522  
    if (i >= l) break;
4523  
    c = s.charAt(i);
4524  
    cc = s.substring(i, Math.min(i+2, l));
4525  
4526  
    // scan for non-whitespace
4527  
    if (c == '\u201C' || c == '\u201D') c = '"'; // normalize quotes
4528  
    if (c == '\'' || c == '"') {
4529  
      char opener = c;
4530  
      ++j;
4531  
      while (j < l) {
4532  
        char _c = s.charAt(j);
4533  
        if (_c == '\u201C' || _c == '\u201D') _c = '"'; // normalize quotes
4534  
        if (_c == opener) {
4535  
          ++j;
4536  
          break;
4537  
        } else if (s.charAt(j) == '\\' && j+1 < l)
4538  
          j += 2;
4539  
        else
4540  
          ++j;
4541  
      }
4542  
      if (j-1 >= i+1) {
4543  
        tok.add(opener + s.substring(i+1, j-1) + opener);
4544  
        i = j;
4545  
        continue;
4546  
      }
4547  
    } else if (Character.isJavaIdentifierStart(c))
4548  
      do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || s.charAt(j) == '\'')); // for things like "this one's"
4549  
    else if (Character.isDigit(c))
4550  
      do ++j; while (j < l && Character.isDigit(s.charAt(j)));
4551  
    else if (cc.equals("[[")) {
4552  
      do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]"));
4553  
      j = Math.min(j+2, l);
4554  
    } else if (cc.equals("[=") && i+2 < l && s.charAt(i+2) == '[') {
4555  
      do ++j; while (j+2 < l && !s.substring(j, j+3).equals("]=]"));
4556  
      j = Math.min(j+3, l);
4557  
    } else if (s.substring(j, Math.min(j+3, l)).equals("..."))
4558  
      j += 3;
4559  
    else if (c == '$' || c == '#')
4560  
      do ++j; while (j < l && Character.isDigit(s.charAt(j)));
4561  
    else
4562  
      ++j;
4563  
4564  
    tok.add(s.substring(i, j));
4565  
    i = j;
4566  
  }
4567  
  
4568  
  if ((tok.size() % 2) == 0) tok.add("");
4569  
  return tok;
4570  
}
4571  
4572  
static void sleepSeconds(long s) {
4573  
  if (s > 0) sleep(s*1000);
4574  
}
4575  
  static Object callOpt(Object o, String method, Object... args) {
4576  
    try {
4577  
      if (o == null) return null;
4578  
      if (o instanceof Class) {
4579  
        Method m = callOpt_findStaticMethod((Class) o, method, args, false);
4580  
        if (m == null) return null;
4581  
        m.setAccessible(true);
4582  
        return m.invoke(null, args);
4583  
      } else {
4584  
        Method m = callOpt_findMethod(o, method, args, false);
4585  
        if (m == null) return null;
4586  
        m.setAccessible(true);
4587  
        return m.invoke(o, args);
4588  
      }
4589  
    } catch (Exception e) {
4590  
      throw new RuntimeException(e);
4591  
    }
4592  
  }
4593  
4594  
  static Method callOpt_findStaticMethod(Class c, String method, Object[] args, boolean debug) {
4595  
    Class _c = c;
4596  
    while (c != null) {
4597  
      for (Method m : c.getDeclaredMethods()) {
4598  
        if (debug)
4599  
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
4600  
        if (!m.getName().equals(method)) {
4601  
          if (debug) System.out.println("Method name mismatch: " + method);
4602  
          continue;
4603  
        }
4604  
4605  
        if ((m.getModifiers() & Modifier.STATIC) == 0 || !callOpt_checkArgs(m, args, debug))
4606  
          continue;
4607  
4608  
        return m;
4609  
      }
4610  
      c = c.getSuperclass();
4611  
    }
4612  
    return null;
4613  
  }
4614  
4615  
  static Method callOpt_findMethod(Object o, String method, Object[] args, boolean debug) {
4616  
    Class c = o.getClass();
4617  
    while (c != null) {
4618  
      for (Method m : c.getDeclaredMethods()) {
4619  
        if (debug)
4620  
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
4621  
        if (m.getName().equals(method) && callOpt_checkArgs(m, args, debug))
4622  
          return m;
4623  
      }
4624  
      c = c.getSuperclass();
4625  
    }
4626  
    return null;
4627  
  }
4628  
4629  
  private static boolean callOpt_checkArgs(Method m, Object[] args, boolean debug) {
4630  
    Class<?>[] types = m.getParameterTypes();
4631  
    if (types.length != args.length) {
4632  
      if (debug)
4633  
        System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
4634  
      return false;
4635  
    }
4636  
    for (int i = 0; i < types.length; i++)
4637  
      if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
4638  
        if (debug)
4639  
          System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
4640  
        return false;
4641  
      }
4642  
    return true;
4643  
  }
4644  
4645  
4646  
static double parseDouble(String s) {
4647  
  return Double.parseDouble(s);
4648  
}
4649  
  static AtomicInteger dialogServer_clients = new AtomicInteger();
4650  
  static boolean dialogServer_printConnects;
4651  
  
4652  
  static Set<String> dialogServer_knownClients = synchroTreeSet();
4653  
  
4654  
  static int startDialogServerOnPortAbove(int port, DialogHandler handler) {
4655  
    while (!startDialogServerIfPortAvailable(port, handler))
4656  
      ++port;
4657  
    return port;
4658  
  }
4659  
  
4660  
  static int startDialogServerOnPortAboveDaemon(int port, DialogHandler handler) {
4661  
    while (!startDialogServerIfPortAvailable(port, handler, true))
4662  
      ++port;
4663  
    return port;
4664  
  }
4665  
  
4666  
  static void startDialogServer(int port, DialogHandler handler) {
4667  
    if (!startDialogServerIfPortAvailable(port, handler))
4668  
      throw fail("Can't start dialog server on port " + port);
4669  
  }
4670  
  
4671  
  static boolean startDialogServerIfPortAvailable(int port, final DialogHandler handler) {
4672  
    return startDialogServerIfPortAvailable(port, handler, false);
4673  
  }
4674  
  
4675  
  static ServerSocket startDialogServer_serverSocket;
4676  
    
4677  
  static boolean startDialogServerIfPortAvailable(int port, final DialogHandler handler, boolean daemon) {
4678  
    ServerSocket serverSocket = null;
4679  
    try {
4680  
      serverSocket = new ServerSocket(port);
4681  
    } catch (IOException e) {
4682  
      // probably the port number is used - let's assume there already is a chat server.
4683  
      return false;
4684  
    }
4685  
    final ServerSocket _serverSocket = serverSocket;
4686  
    startDialogServer_serverSocket = serverSocket;
4687  
4688  
    Thread thread = new Thread("Socket accept port " + port) { public void run() {
4689  
     try {
4690  
      while (true) {
4691  
        try {
4692  
          final Socket s = _serverSocket.accept();
4693  
          
4694  
          String client = s.getInetAddress().toString();
4695  
          if (!dialogServer_knownClients.contains(client) && neq(client, "/127.0.0.1")) {
4696  
            print("connect from " + client + " - clients: " + dialogServer_clients.incrementAndGet());
4697  
            dialogServer_knownClients.add(client);
4698  
          }
4699  
          
4700  
          String threadName = "Handling client " + s.getInetAddress();
4701  
4702  
          Thread t2 = new Thread(threadName) {
4703  
           public void run() {
4704  
            try {
4705  
              final Writer w = new OutputStreamWriter(s.getOutputStream(), "UTF-8");
4706  
              final BufferedReader in = new BufferedReader(
4707  
                new InputStreamReader(s.getInputStream(), "UTF-8"));
4708  
                
4709  
              DialogIO io = new DialogIO() {
4710  
              
4711  
                // This should be the same as #1001076 (talkTo)
4712  
              
4713  
    boolean isLocalConnection() {
4714  
      return s.getInetAddress().isLoopbackAddress();
4715  
    }
4716  
    
4717  
    boolean isStillConnected() {
4718  
      return !(eos || s.isClosed());
4719  
    }
4720  
    
4721  
    void sendLine(String line) { try {
4722  
 
4723  
      w.write(line + "\n");
4724  
      w.flush();
4725  
    
4726  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
4727  
    
4728  
    String readLineImpl() { try {
4729  
 
4730  
      return in.readLine();
4731  
    
4732  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
4733  
    
4734  
    void close() {
4735  
      try {
4736  
        s.close();
4737  
      } catch (IOException e) {
4738  
        // whatever
4739  
      }
4740  
    }
4741  
    
4742  
    Socket getSocket() {
4743  
      return s;
4744  
    }
4745  
    };
4746  
              
4747  
              try {
4748  
                handler.run(io);
4749  
              } finally {
4750  
                s.close();
4751  
              }
4752  
            } catch (IOException e) {
4753  
              print("[internal] " + e);
4754  
            } finally {
4755  
              //print("client disconnect - " + dialogServer_clients.decrementAndGet() + " remaining");
4756  
            }
4757  
           }
4758  
          }; // Thread t2
4759  
          t2.setDaemon(true); // ?
4760  
          t2.start();
4761  
        } catch (SocketTimeoutException e) {
4762  
        }
4763  
      }   
4764  
     } catch (IOException e) {
4765  
       print("[internal] " + e);
4766  
     }
4767  
    }};
4768  
    if (daemon) thread.setDaemon(true);
4769  
    thread.start();
4770  
   
4771  
    //print("Dialog server on port " + port + " started."); 
4772  
    return true;
4773  
  }
4774  
static boolean webAuthed() {
4775  
  return eq(Boolean.TRUE, callOpt(getBot("#1002590"), "currentHttpRequestAuthorized"));
4776  
}
4777  
static boolean isEmpty(Collection c) {
4778  
  return c == null || c.isEmpty();
4779  
}
4780  
4781  
static boolean isEmpty(CharSequence s) {
4782  
  return s == null || s.length() == 0;
4783  
}
4784  
4785  
static boolean isEmpty(Object[] a) {
4786  
  return a == null || a.length == 0;
4787  
}
4788  
4789  
static boolean isEmpty(Map map) {
4790  
  return map == null || map.isEmpty();
4791  
}
4792  
static boolean nempty(Collection c) {
4793  
  return !isEmpty(c);
4794  
}
4795  
4796  
static boolean nempty(CharSequence s) {
4797  
  return !isEmpty(s);
4798  
}
4799  
4800  
static boolean nempty(Object[] o) {
4801  
  return !isEmpty(o);
4802  
}
4803  
4804  
static String callStaticAnswerMethod(List<Class> bots, String s) {
4805  
  for (Class c : bots) try {
4806  
    String answer = callStaticAnswerMethod(c, s);
4807  
    if (!empty(answer)) return answer;
4808  
  } catch (Throwable e) {
4809  
    print("Error calling " + getProgramID(c));
4810  
    e.printStackTrace();
4811  
  }
4812  
  return null;
4813  
}
4814  
4815  
static String callStaticAnswerMethod(Class c, String s) {
4816  
  String answer = (String) callOpt(c, "answer", s, litlist(s));
4817  
  if (answer == null)
4818  
    answer = (String) callOpt(c, "answer", s);
4819  
  return emptyToNull(answer);
4820  
}
4821  
4822  
static String callStaticAnswerMethod(String s, List<String> history) {
4823  
  String answer = (String) callOpt(getMainClass(), "answer", s, history);
4824  
  if (answer == null)
4825  
    answer = (String) callOpt(getMainClass(), "answer", s);
4826  
  return emptyToNull(answer);
4827  
}
4828  
static char unquoteCharacter(String s) {
4829  
  assertTrue(s.startsWith("'") && s.length() > 1);
4830  
  return unquote("\"" + s.substring(1, s.endsWith("'") ? s.length()-1 : s.length()) + "\"").charAt(0);
4831  
}
4832  
4833  
4834  
static boolean equalsIgnoreCase(String a, String b) {
4835  
  return a == null ? b == null : a.equalsIgnoreCase(b);
4836  
}
4837  
static <A> Set<A> synchroTreeSet() {
4838  
  return Collections.synchronizedSet(new TreeSet<A>());
4839  
}
4840  
4841  
static Object mainBot;
4842  
4843  
static Object getMainBot() {
4844  
  return mainBot;
4845  
}
4846  
static int min(int a, int b) {
4847  
  return Math.min(a, b);
4848  
}
4849  
4850  
static double min(double[] c) {
4851  
  double x = Double.MAX_VALUE;
4852  
  for (double d : c) x = Math.min(x, d);
4853  
  return x;
4854  
}
4855  
4856  
static byte min(byte[] c) {
4857  
  byte x = 127;
4858  
  for (byte d : c) if (d < x) x = d;
4859  
  return x;
4860  
}
4861  
static void assertTrue(Object o) {
4862  
  assertEquals(true, o);
4863  
}
4864  
  
4865  
static boolean assertTrue(String msg, boolean b) {
4866  
  if (!b)
4867  
    throw fail(msg);
4868  
  return b;
4869  
}
4870  
4871  
static boolean assertTrue(boolean b) {
4872  
  if (!b)
4873  
    throw fail("oops");
4874  
  return b;
4875  
}
4876  
static int max(int a, int b) {
4877  
  return Math.max(a, b);
4878  
}
4879  
4880  
static long max(int a, long b) {
4881  
  return Math.max((long) a, b);
4882  
}
4883  
4884  
static long max(long a, long b) {
4885  
  return Math.max(a, b);
4886  
}
4887  
4888  
static double max(int a, double b) {
4889  
  return Math.max((double) a, b);
4890  
}
4891  
4892  
static int max(Collection<Integer> c) {
4893  
  int x = Integer.MIN_VALUE;
4894  
  for (int i : c) x = max(x, i);
4895  
  return x;
4896  
}
4897  
4898  
static double max(double[] c) {
4899  
  if (c.length == 0) return Double.MIN_VALUE;
4900  
  double x = c[0];
4901  
  for (int i = 1; i < c.length; i++) x = Math.max(x, c[i]);
4902  
  return x;
4903  
}
4904  
4905  
static byte max(byte[] c) {
4906  
  byte x = -128;
4907  
  for (byte d : c) if (d > x) x = d;
4908  
  return x;
4909  
}
4910  
static Class<?> getClass(String name) {
4911  
  try {
4912  
    return Class.forName(name);
4913  
  } catch (ClassNotFoundException e) {
4914  
    return null;
4915  
  }
4916  
}
4917  
4918  
static Class getClass(Object o) {
4919  
  return o instanceof Class ? (Class) o : o.getClass();
4920  
}
4921  
4922  
static Class getClass(Object realm, String name) { try {
4923  
 
4924  
  return getClass(realm).getClassLoader().loadClass(classNameToVM(name));
4925  
4926  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
4927  
static Class mc() {
4928  
  return getMainClass();
4929  
}
4930  
static boolean neq(Object a, Object b) {
4931  
  return !eq(a, b);
4932  
}
4933  
static <A> A last(List<A> l) {
4934  
  return l.isEmpty() ? null : l.get(l.size()-1);
4935  
}
4936  
static String classNameToVM(String name) {
4937  
  return name.replace(".", "$");
4938  
}
4939  
static String emptyToNull(String s) {
4940  
  return eq(s, "") ? null : s;
4941  
}
4942  
static boolean booleanValue(Object o) {
4943  
  return eq(true, o);
4944  
}
4945  
  static Method findMethod(Object o, String method, Object... args) {
4946  
    try {
4947  
      if (o == null) return null;
4948  
      if (o instanceof Class) {
4949  
        Method m = findMethod_static((Class) o, method, args, false);
4950  
        if (m == null) return null;
4951  
        m.setAccessible(true);
4952  
        return m;
4953  
      } else {
4954  
        Method m = findMethod_instance(o, method, args, false);
4955  
        if (m == null) return null;
4956  
        m.setAccessible(true);
4957  
        return m;
4958  
      }
4959  
    } catch (Exception e) {
4960  
      throw new RuntimeException(e);
4961  
    }
4962  
  }
4963  
4964  
  static Method findMethod_static(Class c, String method, Object[] args, boolean debug) {
4965  
    Class _c = c;
4966  
    while (c != null) {
4967  
      for (Method m : c.getDeclaredMethods()) {
4968  
        if (debug)
4969  
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
4970  
        if (!m.getName().equals(method)) {
4971  
          if (debug) System.out.println("Method name mismatch: " + method);
4972  
          continue;
4973  
        }
4974  
4975  
        if ((m.getModifiers() & Modifier.STATIC) == 0 || !findMethod_checkArgs(m, args, debug))
4976  
          continue;
4977  
4978  
        return m;
4979  
      }
4980  
      c = c.getSuperclass();
4981  
    }
4982  
    return null;
4983  
  }
4984  
4985  
  static Method findMethod_instance(Object o, String method, Object[] args, boolean debug) {
4986  
    Class c = o.getClass();
4987  
    while (c != null) {
4988  
      for (Method m : c.getDeclaredMethods()) {
4989  
        if (debug)
4990  
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
4991  
        if (m.getName().equals(method) && findMethod_checkArgs(m, args, debug))
4992  
          return m;
4993  
      }
4994  
      c = c.getSuperclass();
4995  
    }
4996  
    return null;
4997  
  }
4998  
4999  
  static boolean findMethod_checkArgs(Method m, Object[] args, boolean debug) {
5000  
    Class<?>[] types = m.getParameterTypes();
5001  
    if (types.length != args.length) {
5002  
      if (debug)
5003  
        System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
5004  
      return false;
5005  
    }
5006  
    for (int i = 0; i < types.length; i++)
5007  
      if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
5008  
        if (debug)
5009  
          System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
5010  
        return false;
5011  
      }
5012  
    return true;
5013  
  }
5014  
5015  
5016  
static <A> List<A> synchroList() {
5017  
  return Collections.synchronizedList(new ArrayList<A>());
5018  
}
5019  
5020  
static <A> List<A> synchroList(List<A> l) {
5021  
  return Collections.synchronizedList(l);
5022  
}
5023  
5024  
static List<String> dropPunctuation_keep = litlist("*", "<", ">");
5025  
5026  
static List<String> dropPunctuation(List<String> tok) {
5027  
  tok = new ArrayList<String>(tok);
5028  
  for (int i = 1; i < tok.size(); i += 2) {
5029  
    String t = tok.get(i);
5030  
    if (t.length() == 1 && !Character.isLetter(t.charAt(0)) && !Character.isDigit(t.charAt(0)) && !dropPunctuation_keep.contains(t)) {
5031  
      tok.set(i-1, tok.get(i-1) + tok.get(i+1));
5032  
      tok.remove(i);
5033  
      tok.remove(i);
5034  
      i -= 2;
5035  
    }
5036  
  }
5037  
  return tok;
5038  
}
5039  
5040  
static String dropPunctuation(String s) {
5041  
  return join(dropPunctuation(nlTok(s)));
5042  
}
5043  
static String dropSuffix(String suffix, String s) {
5044  
  return s.endsWith(suffix) ? s.substring(0, l(s)-l(suffix)) : s;
5045  
}
5046  
// start multi-port if none exists in current VM.
5047  
static void startMultiPort() {
5048  
  List mp = getMultiPorts();
5049  
  if (mp != null && mp.isEmpty())
5050  
    callMain(hotwire("#1001672"));
5051  
}
5052  
5053  
5054  
static <A> A assertEquals(Object x, A y) {
5055  
  return assertEquals(null, x, y);
5056  
}
5057  
5058  
static <A> A assertEquals(String msg, Object x, A y) {
5059  
  if (!(x == null ? y == null : x.equals(y)))
5060  
    throw fail((msg != null ? msg + ": " : "") + structure(x) + " != " + structure(y));
5061  
  return y;
5062  
}
5063  
static List<String> nlTok(String s) {
5064  
  return javaTokPlusPeriod(s);
5065  
}
5066  
5067  
5068  
static boolean match(String pat, String s) {
5069  
  return match3(pat, s);
5070  
}
5071  
5072  
static boolean match(String pat, String s, Matches matches) {
5073  
  return match3(pat, s, matches);
5074  
}
5075  
5076  
static void cleanUp(Object c) {
5077  
  if (c instanceof List) { cleanUp((List) c); return; }
5078  
  if (!(c instanceof Class)) return;
5079  
  
5080  
  try { /* pcall 1*/ 
5081  
    // revoke license
5082  
    
5083  
    callOpt(c, "licensed_off");
5084  
    
5085  
    // call custom cleanUp() function
5086  
    
5087  
    try { /* pcall 1*/  callOpt(c, "cleanMeUp"); /* pcall 2 */ } catch (Throwable __e) { printStackTrace(__e); }
5088  
    
5089  
    // remove all virtual bots (does this work?)
5090  
    
5091  
    List androids = (List) getOpt(c, "record_list");
5092  
    List ports = getMultiPorts();
5093  
    if (androids != null)
5094  
      for (Object port : ports)
5095  
        for (Object android : androids)
5096  
          callOpt(android, "dispose"); // heck we'll dispose anything
5097  
5098  
    // sub-cleanup
5099  
    
5100  
    List<WeakReference> classes = (List<WeakReference>) ( getOpt(c, "hotwire_classes"));
5101  
    if (classes != null)
5102  
      for (WeakReference cc : classes) try { /* pcall 1*/ 
5103  
        cleanUp(cc.get());
5104  
      /* pcall 2 */ } catch (Throwable __e) { printStackTrace(__e); }
5105  
  /* pcall 2 */ } catch (Throwable __e) { printStackTrace(__e); }
5106  
}
5107  
5108  
static void cleanUp(List l) {
5109  
  for (Object c : l)
5110  
    cleanUp(c);
5111  
  l.clear();
5112  
}
5113  
5114  
static String standardCredentials() {
5115  
  String user = trim(loadTextFile(new File(userHome(), ".tinybrain/username")));
5116  
  String pass = trim(loadTextFile(new File(userHome(), ".tinybrain/userpass")));
5117  
  if (nempty(user) && nempty(pass))
5118  
    return "&_user=" + urlencode(user) + "&_pass=" + urlencode(pass);
5119  
  return "";
5120  
}
5121  
5122  
static byte[] loadBinaryPage(String url) {
5123  
  try {
5124  
    return loadBinaryPage(new URL(url).openConnection());
5125  
  } catch (IOException e) {
5126  
    throw new RuntimeException(e);
5127  
  }
5128  
}
5129  
5130  
static String trim(String s) { return s == null ? null : s.trim(); }
5131  
5132  
static String urlencode(String x) {
5133  
  try {
5134  
    return URLEncoder.encode(unnull(x), "UTF-8");
5135  
  } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); }
5136  
}
5137  
5138  
public static void copyFile(File src, File dest) { try {
5139  
 
5140  
  mkdirsForFile(dest);
5141  
  FileInputStream inputStream = new FileInputStream(src.getPath());
5142  
  FileOutputStream outputStream = new FileOutputStream(dest.getPath());
5143  
  try {
5144  
    copyStream(inputStream, outputStream);
5145  
    inputStream.close();
5146  
  } finally {
5147  
    outputStream.close();
5148  
  }
5149  
5150  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
5151  
5152  
static void copyStream(InputStream in, OutputStream out) { try {
5153  
 
5154  
  byte[] buf = new byte[65536];
5155  
  while (true) {
5156  
    int n = in.read(buf);
5157  
    if (n <= 0) return;
5158  
    out.write(buf, 0, n);
5159  
  }
5160  
5161  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
5162  
5163  
static String hideCredentials(String url) {
5164  
  return url.replaceAll("&_pass=[^&]*", "&_pass=<hidden>");
5165  
}
5166  
5167  
static class MultiPort {
5168  
  static class Resp {
5169  
    long virtualPort;
5170  
    String botName;
5171  
    Object responder;
5172  
  }
5173  
  
5174  
  List<Resp> responders = new ArrayList();
5175  
  Android3 android;
5176  
  long nextVirtualPort = 1;
5177  
  
5178  
  MultiPort() {
5179  
    synchronized(getJavaX()) {
5180  
      if (!getMultiPorts().isEmpty()) return;
5181  
      if (!(Boolean) call(getJavaX(), "addMultiPort", this)) {
5182  
        // XXX - hope this still works
5183  
        Android3 android = new Android3("Multi-Port at " + getVMID() + ".");
5184  
        android.useMultiPort = false;
5185  
        android.incomingSilent = true;
5186  
        android.console = false;
5187  
        android.daemon = true;
5188  
        makeAndroid3(android);
5189  
      }
5190  
    }
5191  
    //print("Dynamic Multi-Port registered with VM. Now " + l(getMultiPorts()) + " multi-port(s).");
5192  
  }
5193  
  
5194  
  String answer(String s) {
5195  
    Matches m = new Matches();
5196  
    
5197  
    if (match3("list bots", s, m)) {
5198  
      return structure(listBotsWithPort());
5199  
    }
5200  
    
5201  
    if (match3("has bot *", s, m)) {
5202  
      String name = m.unq(0);
5203  
      return findResponder(name) != null ? "yes" : "no";
5204  
    }
5205  
    
5206  
    if (match3("silent", s, m)) {
5207  
      android.incomingSilent = true;
5208  
      return "OK.";
5209  
    }
5210  
    
5211  
    if (match3("unsilent", s, m)) {
5212  
      android.incomingSilent = false;
5213  
      return "OK.";
5214  
    }
5215  
    
5216  
    if (match3("please forward to bot *: *", s, m)) {
5217  
      String bot = unquote(m.m[0]);
5218  
      String line = unquote(m.m[1]);
5219  
      Object responder = findResponder(bot);
5220  
      if (responder == null)
5221  
        return "Error: bot not found";
5222  
      else
5223  
        return (String) call(responder, "answer", line, litlist(s));
5224  
    }
5225  
    
5226  
    return null;
5227  
  }
5228  
  
5229  
  synchronized List<String> listBots() {
5230  
    List<String> list = new ArrayList();
5231  
    for (Resp r : responders)
5232  
      list.add(r.botName);
5233  
    return list;
5234  
  }
5235  
  
5236  
  synchronized Map<Long,String> listBotsWithPort() {
5237  
    Map<Long,String> map = new HashMap();
5238  
    for (Resp r : responders)
5239  
      map.put(r.virtualPort, r.botName);
5240  
    return map;
5241  
  }
5242  
  
5243  
  synchronized Object findResponder(String botName) {
5244  
    if (isInteger(botName)) {
5245  
      long vport = parseLong(botName);
5246  
      for (Resp r : responders)
5247  
        if (r.virtualPort == vport)
5248  
          return r.responder;
5249  
    } else {
5250  
      for (Resp r : responders)
5251  
        if (startsWithIgnoreCase(r.botName, botName))
5252  
          return r.responder;
5253  
    }
5254  
    return null;
5255  
  }
5256  
      
5257  
  synchronized long addResponder(String botName, Object responder) {
5258  
    Resp r = new Resp();
5259  
    r.virtualPort = nextVirtualPort++;
5260  
    r.botName = botName;
5261  
    r.responder = responder;
5262  
    responders.add(r);
5263  
    return r.virtualPort;
5264  
  }
5265  
  
5266  
  synchronized void removeResponder(Object responder) {
5267  
    for (int i = 0; i < l(responders); i++)
5268  
      if (responders.get(i).responder == responder) {
5269  
        //print("Responder removed.");
5270  
        responders.remove(i);
5271  
        return;
5272  
      }
5273  
  }
5274  
  
5275  
  synchronized void removePort(long virtualPort) {
5276  
    for (int i = 0; i < l(responders); i++)
5277  
      if (responders.get(i).virtualPort == virtualPort) {
5278  
        //print("Virtual port " + virtualPort + " removed.");
5279  
        responders.remove(i);
5280  
        return;
5281  
      }
5282  
  }
5283  
}
5284  
5285  
static interface Producer<A> {
5286  
  public A next();
5287  
}
5288  
5289  
static String getVMID() {
5290  
  return (String) get(getJavaX(), "vmID");
5291  
}
5292  
5293  
static boolean startsWithIgnoreCase(String a, String b) {
5294  
  return a != null && a.regionMatches(true, 0, b, 0, b.length());
5295  
}
5296  
5297  
static <A> A getAndClearThreadLocal(ThreadLocal<A> tl) {
5298  
  A a = tl.get();
5299  
  tl.set(null);
5300  
  return a;
5301  
}
5302  
5303  
static Map synchroHashMap() {
5304  
  return Collections.synchronizedMap(new HashMap());
5305  
}
5306  
5307  
// DUMMY!
5308  
static <A extends Map> A _registerWeakMap(A o) {
5309  
  return o;
5310  
}
5311  
5312  
static String tb_mainServer_default = "http://code.botcompany.de:8081";
5313  
5314  
static String tb_mainServer() {
5315  
  return tb_mainServer_default;
5316  
}
5317  
5318  
}

download  show line numbers  debug dex  old transpilations   

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

No comments. add comment

Snippet ID: #1004182
Snippet name: x30 for Android [stage 3, Java, LIVE]
Eternal ID of this version: #1004182/29
Text MD5: 153c5b3edb77692545c6edde1c6b0f7a
Transpilation MD5: 5804d83dda0e381eb6d30c3e87bf664c
Author: stefan
Category: javax / android
Type: JavaX source code (Android)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2019-07-23 15:48:15
Source code size: 169787 bytes / 5318 lines
Pitched / IR pitched: No / No
Views / Downloads: 5662 / 6492
Version history: 28 change(s)
Referenced in: [show references]