如何将动态编译的字节重新编码为文本?

时间:2014-04-28 04:58:13

标签: java dynamic character-encoding compilation

考虑以下(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;
    }
}

在compile.getTask()。call()上逐步执行代码,这里发生的第一件事就是调用getJavaFileForOutput(),然后调用getClassLoader()方法来加载类,编译后的字节写入控制台。

为什么getClassLoader()方法中的println会产生我工作的编译字节码的合并(主要是字符串,看起来实际的字节码指令关键字不在这里)和随机乱码?这让我相信我使用的UTF太短,所以我尝试了UTF-16,它看起来或多或少相似。如何将字节编码回文本?我知道使用SimpleJavaFileManager会很简单但我需要能够使用这个缓存示例(当然没有可能的内存泄漏)用于性能目的。

编辑: 是的,编译后的代码可以完成类加载和运行。

1 个答案:

答案 0 :(得分:1)

  

为什么getClassLoader()方法中的println会产生我工作的编译字节码的合并(主要是字符串,看起来实际的字节码指令关键字不在这里)和随机乱码?

如果没有看到所谓的&#34;随机乱码&#34;,我会猜测你所看到的是一个格式良好的二进制内容,它已经被解码了#34;作为某些字符集中的字符串。

那无法上班。它是一种二进制格式,您不能期望将其转换为文本,并将其显示为可读的内容。

(对于它的价值,一个&#34; .class&#34;文件不包含JVM操作码的关键字,除了#34; .exe&#34;文件将包含机器的关键字说明。它是二进制!)


如果要以文本形式查看已编译的代码,请将该字节数组中的字节保存到文件中,然后使用javap实用程序查看它。 (我将让您查找javap命令的命令行语法...)