Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create an object from a string in Java (how to eval a string)?

Tags:

java

eval

I know eval is "evil", but I'm using it in a way that the user can't ever abuse it.

Let's say I've got a string "new Integer(5)". I want to do something such that I can set a variable, let's say foo, to new Integer(5). Something like

Integer foo;
String bar = "new Integer(5)"
*magic happens*
System.out.println(foo) -> 5

I've looked around and it looks like I have a few options. Can the getSystemJavaCompiler() method in ToolProvider do this? Or should I use BeanShell? Or is there something else? Note that this is from a string, not a file.

like image 442
swampsjohn Avatar asked Dec 17 '22 06:12

swampsjohn


2 Answers

Here's a possible way to get most of the way there via using javax.tools. Note that this code is rather long and not exactly the most efficient or portable way to do this, but you should get the idea.

import javax.tools.*;
import java.util.*;
import java.io.*;
import java.lang.reflect.*;

public class Test {
  public static void main(String[] args) throws Exception {
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    JavaFileObject fileObj = 
      new StringJavaFileObject("public class InterpTest { public static void test() { System.out.println(\"Hello World\"); } }");
    List<JavaFileObject> tasks = new ArrayList<JavaFileObject>();
    tasks.add(fileObj);

    JavaFileManager defFileMgr = compiler.getStandardFileManager(null, null, null);
    MemoryJavaFileManager fileMgr = new MemoryJavaFileManager(defFileMgr);
    compiler.getTask(null, fileMgr, null, null, null, tasks).call();

    ClassLoader loader = new ByteArrayClassLoader();
    Class clazz = loader.loadClass("InterpTest");
    Method method = clazz.getMethod("test");
    method.invoke(null);
  }

  public static class StringJavaFileObject extends SimpleJavaFileObject {
    protected String str;

    public StringJavaFileObject(String str) {
      super(java.net.URI.create("file:///InterpTest.java"), JavaFileObject.Kind.SOURCE);
      this.str = str;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncErrors) {
      return str;
    }
  }

  public static class MemoryJavaFileObject extends SimpleJavaFileObject {
    public static ByteArrayOutputStream out = new ByteArrayOutputStream();

    public MemoryJavaFileObject(String uri, JavaFileObject.Kind kind) {
      super(java.net.URI.create(uri), kind);
    }

    @Override
    public OutputStream openOutputStream() {
      return out;
    }
  }

  public static class ByteArrayClassLoader extends ClassLoader {
    public Class findClass(String name) {
      byte[] bytes = MemoryJavaFileObject.out.toByteArray();
      return super.defineClass(name, bytes, 0, bytes.length);
    }
  }

  public static class MemoryJavaFileManager implements JavaFileManager {
    protected JavaFileManager parent;

    public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
      return new MemoryJavaFileObject("file:///InterpTest.class", kind);
    }

    public MemoryJavaFileManager(JavaFileManager parent) { this.parent = parent; }
    public void close() throws IOException { parent.close(); }
    public void flush() throws IOException { parent.flush(); }
    public ClassLoader getClassLoader(JavaFileManager.Location location) { return parent.getClassLoader(location); }
    public FileObject getFileForInput(JavaFileManager.Location location, String packageName, String relName) throws IOException { return parent.getFileForInput(location, packageName, relName); }
    public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relName, FileObject sibling) throws IOException { return parent.getFileForOutput(location, packageName, relName, sibling); }
    public JavaFileObject getJavaFileForInput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind) throws IOException { return parent.getJavaFileForInput(location, className, kind); }
    public boolean handleOption(String current, Iterator<String> remaining) { return parent.handleOption(current, remaining); }
    public boolean hasLocation(JavaFileManager.Location location) { return parent.hasLocation(location); }
    public String inferBinaryName(JavaFileManager.Location location, JavaFileObject file) { return parent.inferBinaryName(location, file); }
    public boolean isSameFile(FileObject a, FileObject b) { return parent.isSameFile(a, b); }
    public Iterable<JavaFileObject> list(JavaFileManager.Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException { return parent.list(location, packageName, kinds, recurse); }
    public int isSupportedOption(String option) { return parent.isSupportedOption(option); }
  }
}
like image 110
toluju Avatar answered Feb 16 '23 00:02

toluju


I would use a scripting language like beanshell, jruby, jython, etc.

like image 37
John Doe Avatar answered Feb 15 '23 23:02

John Doe