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] |