2014-04-28 28 views
0

考慮以下(Sourced primarily from here)如何將動態編譯的字節重新編碼爲文本?

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
JavaFileManager manager = new MemoryFileManager(compiler.getStandardFileManager(null, null, null)); 

compiler.getTask(null, manager, null, null, null, sourceScripts).call(); //sourceScripts is of type List<ClassFile> 

而下面的文件管理器:

public class MemoryFileManager extends ForwardingJavaFileManager<JavaFileManager> { 
    private HashMap< String, ClassFile > classes = new HashMap<>(); 

    public MemoryFileManager(StandardJavaFileManager standardManager) { 
     super(standardManager); 
    } 

    @Override 
    public ClassLoader getClassLoader(Location location) { 
     return new SecureClassLoader() { 
      @Override 
      protected Class<?> findClass(String className) throws ClassNotFoundException { 
       if (classes.containsKey(className)) { 
        byte[ ] classFile = classes.get(className).getClassBytes(); 
        System.out.println(new String(classFile, "utf-8")); 
        return super.defineClass(className, classFile, 0, classFile.length); 
       } else throw new ClassNotFoundException(); 
      } 
     }; 
    } 

    @Override 
    public ClassFile getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) { 
     if (classes.containsKey(className)) return classes.get(className); 
     else { 
      ClassFile classObject = new ClassFile(className, kind); 
      classes.put(className, classObject); 
      return classObject; 
     } 
    } 
} 

public class ClassFile extends SimpleJavaFileObject { 
    private byte[ ] source; 
    protected final ByteArrayOutputStream compiled = new ByteArrayOutputStream(); 

    public ClassFile(String className, byte[ ] contentBytes) { 
     super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); 
     source = contentBytes; 
    } 

    public ClassFile(String className, CharSequence contentCharSequence) throws UnsupportedEncodingException { 
     super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); 
     source = ((String)contentCharSequence).getBytes("UTF-8"); 
    } 

    public ClassFile(String className, Kind kind) { 
     super(URI.create("string:///" + className.replace('.', '/') + kind.extension), kind); 
    } 

    public byte[ ] getClassBytes() { 
     return compiled.toByteArray(); 
    } 

    public byte[ ] getSourceBytes() { 
     return source; 
    } 

    @Override 
    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws UnsupportedEncodingException { 
     return new String(source, "UTF-8"); 
    } 

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

單步調試代碼,在compiler.getTask()()調用,這裏發生的第一件事情調用getJavaFileForOutput(),然後調用getClassLoader()方法來加載類,這會在編譯後的字節中寫入控制檯。

爲什麼getClassLoader()方法中的println會產生我的工作編譯字節碼(主要是字符串,看起來實際的字節碼指令關鍵字不在這裏)和隨機亂碼的混合?這使我相信我使用的是太短的UTF,所以我嘗試了UTF-16,它看起來或多或少相似。如何將字節編碼迴文本?我知道使用SimpleJavaFileManager將會很簡單,但我需要能夠使用這個緩存示例(當然,不會出現可能的內存泄漏)以達到性能目的。

編輯: 是的,編譯後的代碼的類加載和完美運行。

回答

1

爲什麼在getClassLoader()方法的println得到我的工作編譯的字節代碼的混合體(主要是字符串,它出現在實際的字節碼指令的關鍵字是不是在這裏)和隨機亂碼?

沒有看到所謂的「隨機胡言亂語」,我猜測,你所看到的是,已經被「解碼」在某些字符集的String類文件的形成以及二進制內容。

這是行不通的。這是一種二進制格式,你不能指望把它變成這樣的文本,並讓它顯示爲可讀的東西。 (對於它的價值,「.class」文件不會包含JVM操作碼的關鍵字,任何超過「.exe」文件都將包含機器指令的關鍵字。它是binary!)


如果你想看到以文本形式編譯代碼,然後保存字節在字節數組到一個文件中,並使用javap工具來看待它。 (我會留給你看命令的命令行語法...)

+0

啊哈,這是一個主要的銳利人。抱歉。無論如何,在那種情況下,他們的編碼是否有某種解碼器?我可以在eclipse中打開.class文件並列出操作碼,因此它必須是可能的。 – rpg711