Selbsterstellender Code

Hiho,

ich habe mich mal wieder mit einem kleinen aber feinen Problem beschäftigt → sich selbst erstellender JavaCode.

Ziel soll sein, erst zur Laufzeit zu bestimmen was mein Programm machen soll. Dann wird die Klasse erstellt, kompiliert und in dem aufrufenden Programm wieder verwendet.

Ein kleines Beispiel:

public static void main(String[] args) throws IOException,
			MalformedURLException, ClassNotFoundException,
			InstantiationException, IllegalAccessException {

		File file = new File(
				"dynamic\\src\\Test.java");
		BufferedWriter bw = new BufferedWriter(new FileWriter(file));

		StringBuffer ausgabe = new StringBuffer();
		Random rand = new Random(System.currentTimeMillis());

		for (int i = 0; i < 10; i++)
			ausgabe.append(rand.nextInt(100) + "\
");

		bw.write("public class Test implements MyCode {
");
		bw.write("public void print() { 
");
		bw.write("System.out.println(\"" + ausgabe + "\");
");
		bw.write("}
");
		bw.write("}");

		bw.flush();
		bw.close();
		
		compile().print();
	}
	
	private static MyCode compile() throws MalformedURLException, ClassNotFoundException,
	InstantiationException, IllegalAccessException{
		
		com.sun.tools.javac.Main.compile(new String[] { "-classpath", "bin",
				"-d", "dynamic\\src",
				"dynamic\\src\\Test.java" });

		File classes = new File("dynamic\\src\\");

		URLClassLoader loader = new URLClassLoader(new URL[] { classes.toURI()
				.toURL() }, MyCode.class.getClassLoader());
		Class clazz = loader.loadClass("Test");

		return (MyCode) clazz.newInstance();
	}

Das Interface MyCode definiert nur die Methode public void print();.

Somit wäre es nun auch theoretisch (um die Wahl des Subforums zu begründen) möglich, den Nutzer sich sein Programm zur Laufzeit bauen zu lassen. :wink:

bye Saxony

Achso noch ein Hinweis:

man muss <jdk_home>/lib/tools.jar verwenden um an den javac Compiler zu kommen (com.sun.tools.javac.Main.compile()).

bye Saxony

Hiho,

ich habe das On-The-Fly Erstellen mal noch von bereits zur Compilezeit existierenden Interfaces losgelöst.
Ist eigentlich das Gleiche - nur noch mit etwas Reflection drumrum.

public static void main(String[] args) throws IOException,
			MalformedURLException, ClassNotFoundException,
			InstantiationException, IllegalAccessException,
			NoSuchMethodException, InvocationTargetException {

		StringBuilder arg = new StringBuilder();
		StringBuilder methodenName1 = new StringBuilder();
		StringBuilder methodenName2 = new StringBuilder();
		StringBuilder className = new StringBuilder();
		StringBuilder argName = new StringBuilder();
		Random rand = new Random(System.currentTimeMillis());

		int length = rand.nextInt(20) + 1;

		className.append((char) (rand.nextInt(26) + 65));
		
		for (int i = 0; i < length; i++) {
			className.append((char) (rand.nextInt(26) + 97));
			methodenName1.append((char) (rand.nextInt(26) + 97));
			methodenName2.append((char) (rand.nextInt(26) + 97));
			argName.append((char) (rand.nextInt(26) + 97));
		}
		
		length = rand.nextInt(100) + 1;

		arg.append("{");
		for (int i = 0; i < length; i++)
			arg.append(rand.nextInt(100) + ",");
		
		arg.append(rand.nextInt(100) + "};");

		File file = new File("dynamic\\src\\" + className + ".java");
		BufferedWriter bw = new BufferedWriter(new FileWriter(file));

		bw.write("public class " + className + " {
");
		bw.write("	public void " + methodenName1 + "() { 
");
		bw.write("		int[] a = new int[] " + arg + "
");
		bw.write("		" + methodenName2 + "(a);
");
		bw.write("	}
");
		bw.write("	public void " + methodenName2 + "(int[] " + argName + ") { 
");
		bw.write("		int tmp = 0;
");
		int operation = rand.nextInt(2) + 1;
		bw.write("		for(int i = 0; i < " + argName + ".length; i++) tmp "
		+ ((operation==1)?"+":"-") + "= " +  argName + "**;
");
		bw.write("		System.out.println(tmp);
");
		bw.write("	}
");
		bw.write("}");

		bw.flush();
		bw.close();

		Class clazz = compile(className.toString());

		Method method = clazz.getMethod(methodenName1.toString(), null);

		method.invoke(clazz.getConstructor(null).newInstance(null));
	}

	private static Class compile(String aName) throws MalformedURLException,
			ClassNotFoundException, InstantiationException,
			IllegalAccessException {

		com.sun.tools.javac.Main.compile(new String[] { "-classpath", "bin",
				"-d", "dynamic\\src", "dynamic\\src\\" + aName + ".java" });

		File classes = new File("dynamic\\src\\");

		URLClassLoader loader = new URLClassLoader(new URL[] { classes.toURI()
				.toURL() }, Compile.class.getClassLoader());
		Class clazz = loader.loadClass(aName);

		return clazz;
	}

bye Saxony

Und wozu soll das gut sein? Also was ist der Hintergrund?

Das könnte man verwenden wie ein „eval“ was es in diversen Scriptsprachen gibt.

Aber auch da gilt: „eval is evil“ :stuck_out_tongue_winking_eye:

Gut Schuß
VuuRWerK :wink:

Die Idee dazu kam mir im Zusammenhang mit meinem BF Interpreter!

Ich will nun ein Programm schreiben, welches sich selber eine Programmiersprache ausdenkt und dazu ebenfalls gleich den Interpreter dazu schreibt. Ebenso wie es zufälligen SourceCode für die neue Sprache schreiben soll. Der Rest vom Programm läuft dann je nach zufällig erzeugtem SourceCode der neuen Sprache ab. Ich habe nur ne Main und lass mir eine neue Sprache bauen, lass den Interpreter dazu schreiben, dann erzeugt es mir ein (zufälliges) Programm in der neuen Sprache und führt es mit dem kurz vorher erzeugten Interpreter aus.

Das ist eigentlich die Idee dahinter. :wink:

Und das hier ist also die Vorbetrachtung zur Problematik.

bye Saxony

Bitte?? Wie soll sich ein Programm eine Sprache ausdenken? Per Zufall oder wie? :smiley:

Sei’s drum, aber ich denke das schwierige ist dann nicht, das „Generat“ zu kompilieren und per Classloader zu laden, sondern eher die Frage, wie du das Generat erstellen willst.

Wahrscheinlich ist Java dabei auch ziemlich umstaendlich. Wie waers mit ner einfachen VM?

Hier noch was Interessantes dazu: Tierra (computer simulation) - Wikipedia

Dafuer gibts Tausende Clones und unterschiedlichste VM-Befehlssaetze. Im Grunde ist BF und Cow nichts anderes als ein solcher Befehlssatz, der „menschenlesbar“ als ASCII und nicht als Binaercode abgespeichert wird.

Edit: Nochn Link von nem aktiven Projekt: Avida - Wikipedia

[QUOTE=Saxony]
Ich will nun ein Programm schreiben, welches sich selber eine Programmiersprache ausdenkt und dazu ebenfalls gleich den Interpreter dazu schreibt. Ebenso wie es zufälligen SourceCode für die neue Sprache schreiben soll. Der Rest vom Programm läuft dann je nach zufällig erzeugtem SourceCode der neuen Sprache ab. Ich habe nur ne Main und lass mir eine neue Sprache bauen, lass den Interpreter dazu schreiben, dann erzeugt es mir ein (zufälliges) Programm in der neuen Sprache und führt es mit dem kurz vorher erzeugten Interpreter aus.[/QUOTE]

Wow.
Kann man damit dann intelligente Programme “züchten”?

Hiho,

nuja das war ja erstma ne fixe Idee die mir da kam! :slight_smile:
Der Inhalt dieses Thread dient eigentlich der Erarbeitung wie ich mir einen Interpreter zur Laufzeit baue, kompiliere und benutze.

Wie die zu findende Sprache erstellt wird weiß ich noch nicht genau. Für den Anfang werden es wohl mods von BF sein, um den Interpreterbau zu üben.
Also gibt es erst einmal relativ strenge Vorgaben für den neuen Syntax.

Ein derzeit weitaus größeres Problem ist sicherlich die mir von meiner Freundin zur Verfügung gestellte Zeit für dieses Problem. :smiley:

bye Saxony

Eigentlich ist der code ja nicht wirklich selbstmodifizierend - er erstellt ja nur neuen Code und compiliert ihn -eigentlich ist das eher Metaprogrammierung. Aber wirklich selbstmodifizierender Code ist schwierig, weil man diesen verf***ten ClassLoader nicht überreden kann, Code neu zu laden…

import java.io.*;

public class SelfModify
{
    private static SMClassLoader classLoader;

    public static void main(String args[])
    {
        SelfModify selfModify = new SelfModify();

        System.out.println(selfModify);

        selfModify.modify();

        try
        {
            Class selfModifyClass = classLoader.loadClass("SelfModify");
            Object selfModifyObject = selfModifyClass.newInstance();
            System.out.println(selfModifyObject);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    public String toString()
    {
        return "Hello ";
    }




    //========================================================================
    // Warning: Here be dragons!

    private static class SMClassLoader extends ClassLoader
    {
        private byte data[];

        public SMClassLoader()
        {
            super(null);
        }

        public void setClassData(byte data[])
        {
            this.data = data;
        }

        public Class findClass(String name) throws ClassNotFoundException
        {
            if (name.equals("SelfModify"))
            {
                return defineClass(name, data, 0, data.length);
            }
            else
            {
                return super.findClass(name);
            }
        }

    };

    public void modify()
    {
        classLoader = new SMClassLoader();
        InputStream inputStream = ClassLoader.getSystemResourceAsStream("SelfModify.class");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try
        {
            int c = -1;
            while ((c=inputStream.read())!=-1) baos.write(c);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        byte code[] = baos.toByteArray();
        //System.out.println("Stream: "+inputStream);
        //System.out.println("bytes : "+new String(code));
        replace(code, new byte[]{'H','e','l','l','o' },
                      new byte[]{'w','o','r','l','d' });
        //System.out.println("bytes : "+new String(code));
        classLoader.setClassData(code);
    }

    private void replace(byte b[], byte sb[], byte tb[])
    {
        for (int i=0; i<b.length; i++)
        {
            boolean found = true;
            for (int j=0; j<sb.length; j++)
            {
                if (b[i+j]!=sb[j])
                {
                    found = false;
                    break;
                }
            }
            if (found)
            {
                for (int j=0; j<sb.length; j++)
                {
                    b[i+j] = tb[j];
                }
            }
        }

    }
}

(Nicht so ernst nehmen :wink: )

Hast recht. Hab mal den Titel geändert! :wink:

[edit]
Hmm leider kann man nur PostTitles ändern aber nicht mehr das ThreadThema selber. Nuja :slight_smile:
[/edit]

bye Saxony

Hab es mal geändert :wink:

Gut Schuß
VuuRWerK :wink: