mardi 9 mai 2017

Extending the String class

A friend of mine asked me, if there was any way to extend the String class in Java. The usual answer would be no, because the String class is final, but I tried to play around with the sun.misc.Unsafe class, to achieve this.

My idea was to modify the String class and remove the final flag, and then compile a class extending String at runtime. I was able to remove the final flag, but compilation of the new class resulted in a JVM crash. Is there any way to fix this? Is there any other way to achieve what I am trying todo, without additional JVM parameters? Would it maybe be possible to load a class not extending String, and later add the 'extends String' to the class signature?

The code I am using:

public static void main(String[] args) throws Exception {
    Field f = Unsafe.class.getDeclaredField("theUnsafe");
    f.setAccessible(true);
    Unsafe unsafe = (Unsafe) f.get(null);
    Class cs = String.class;
    long offset = 72;//http://ift.tt/2qXRft3
    unsafe.putShort(cs, offset, (short) (unsafe.getShort(cs, offset) & ~Modifier.FINAL));
    if((String.class.getModifiers() & Modifier.FINAL) != 0) {
        System.out.println("Class is final!");
        System.exit(0);
    }
    else {
        System.out.println("Class is not final");
    }
    File file = new File("StringExtend.java");
    Class c = compile(file);
    System.out.println(c.newInstance());
}

public static Class compile(File file) {
    try {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        List<String> optionList = new ArrayList<>();
        boolean success;
        try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) {
            Iterable<? extends JavaFileObject> units;
            units = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(file));
            JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, optionList, null, units);
            success = task.call();
        }
        if (success) {
            return load(new File(file.getAbsolutePath().substring(0, file.getAbsolutePath().length() - 5) + ".class"));
        } else {
            return null;
        }
    } catch (IOException ex) {
        ex.printStackTrace();
        return null;
    }
}

private static Class load(File file) {
    try {
        URL url = new File(file.getAbsolutePath().substring(0, file.getAbsolutePath().length() - file.getName().length())).toURI().toURL();
        ClassLoader cl = new URLClassLoader(new URL[]{url}, RuntimeCompiler.class.getClassLoader());
        Class c = cl.loadClass(file.getName().substring(0, file.getName().length() - 6));
        return c;
    } catch (MalformedURLException | ClassNotFoundException ex) {
        ex.printStackTrace();
        return null;
    }
}

StringExtend.java

public class StringExtend extends String {

    public boolean equals(Object o) {
        return true;
    }
}





Aucun commentaire:

Enregistrer un commentaire