Действительно ли возможно сделать программу Java, которая печатает ее исходный код в новый файл, и компилирует его и запускает скомпилированную программу?
Да, это возможно. Тривиальная реализация была бы такой: исходный код содержал бы себя в строке, сохранял строку в файл и заполнял ее собственную строку той же строкой (в противном случае начальная строка будет иметь бесконечный размер из-за рекурсивного способа этой реализации), скомпилируйте файл и запустите скомпилированный файл (который, в свою очередь, сделает то же самое).
Нетривиальные реализации значительно сложнее.
Да - не забудьте использовать JDK вместо JRE:
Объедините файлы исходного кода приложения с приложением. Приложение будет копировать исходные файлы в новый набор файлов исходного кода, компилировать новые исходные файлы, связывать новый исходный код с новыми файлами классов в новое приложение, а затем создавать новое приложение.
или
Объедините декомпилятор с приложением. Приложение запускало бы декомпилятор для своих собственных файлов классов, чтобы сгенерировать новые файлы исходного кода, скомпилировать новые исходные файлы, связать декомпилятор с новыми файлами классов в новое приложение, а затем создать новое приложение.
Sure it works - Have a look at rosetta code and navigate to Quine, which is a self-referential program that can, without any external access, output its own source.
Есть один пример quine на Java.
Для этого можно использовать API компилятора Java (JSR-199). Ниже приведен код из JSR-199, который компилирует код из строки (немного изменен, чтобы он компилировался). Код фактически компилирует исходный код из String
в массив байтов (т.е. он не записывает на диск), загружает его и затем выполняет через отражение:
MemoryFileManager.java
: A файловый менеджер для компиляции строк в байтовые массивы. ByteArrayClassLoader.java
: загрузчик классов, который загружает классы из байтовых массивов. CompileFromString.java
: класс, объединяющий все вместе. Это могло быть отправной точкой (кредиты Питеру Ван дер Ае, первоначальному автору).
Кстати, вам, конечно же, понадобится JDK для использования этого API.
Я точно не знаю, что вам нужно, но я думаю, что вы можете использовать BeanShell . BeanShell - это интерпретатор. Вы можете запустить некомпилированный Java-код (вы даете ему строку с кодом, и он ее запускает).
Конечно, если вы действительно хотите делать то, что написали, машине, на которой выполняется программа, требуется JDK для компиляции вашей программы.
Надеюсь, это поможет
Я не думаю, что это будет работать на Java. Разве это не связано с перезаписью файла работающего класса.
Предположим, ваша программа находится на Quine.java и скомпилирована в Quine.class.
Теперь Quine.class попытается записать свой вывод в Quine.java (пока все хорошо) и скомпилировать его в Quine.class. Это будет проблемой, поскольку Quine.class уже работает
Ладно, можно было бы сделать автозапуск. Наслаждайтесь безумием. Запускайте на свой страх и риск.
Да, это возможно, потому что я действительно написал это. Он не делает часть RUN (это слишком безумно, потому что, как уже говорили другие, это вызовет бесконечный цикл), но вот он: Quine.java
import java.io.*;
public class Quine {
public static void main(String[] args) throws Exception {
char q = 34;
String out = "Quine$";
String text = (
"import java.io.*; " +
"public class [OUT] { " +
"public static void main(String[] args) throws Exception { " +
"char q = 34; String out = `[OUT]$`; String text = `[TEXT]`; " +
"PrintWriter pw = new PrintWriter(out + `.java`); " +
"pw.format(text, 34, out, text); " +
"pw.close(); Runtime runtime = Runtime.getRuntime(); " +
"runtime.exec(`javac ` + out + `.java`).waitFor(); " +
"runtime.exec(`java ` + out); " +
"} " +
"}"
).replace("`", "%1$c").replace("[OUT]", "%2$s").replace("[TEXT]", "%3$s");
PrintWriter pw = new PrintWriter(out + ".java");
pw.format(text, 34, out, text);
pw.close();
Runtime runtime = Runtime.getRuntime();
runtime.exec("javac " + out + ".java").waitFor();
runtime.exec("java " + out);
}
}
Итак, вот как начать безумие:
javac Quine.java
для компиляцииjava Quine
для запуска.
Quine$
Quine.java
максимально читабелен, поэтому основное отличие от Quine$.java
- это форматирование и 3x замена
. незначительное отличие в том, что Quine$.java
имеет out
, установленный на Quine$$
. Quine$
будет производить, компилировать и запускать Quine$$
Quine$$
будет производить, компилировать и запускать Quine$$$
Quine$$$
будет производить, компилировать и запускать Quine$$$$
Обратите внимание, что это не делает никакого реверс-инжиниринга или обмана путем чтения .java
исходного кода и т.д. Quine
- это генератор quine, потому что он производит другой код в другом формате, но Quine$
- это практически настоящий самодостаточный quine: он действительно воспроизводит себя, он просто переименовывает его в Quine$$
(который воспроизводит себя и переименовывает в Quine$$$
и т.д.).
Так что технически бесконечного цикла нет: в конце концов он остановится, когда файловая система не сможет обработать еще один $
. Я смог вручную остановить это безумие, принудительно удалив все Quine$*
файлы, но работайте на свой страх и риск!!!