Search in sources :

Example 1 with RETAIN_CLASS_REFERENCE

use of java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE in project jOOR by jOOQ.

the class Compile method compile.

static Class<?> compile(String className, String content, CompileOptions compileOptions) {
    Lookup lookup = MethodHandles.lookup();
    ClassLoader cl = lookup.lookupClass().getClassLoader();
    try {
        return cl.loadClass(className);
    } catch (ClassNotFoundException ignore) {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        if (compiler == null)
            throw new ReflectException("No compiler was provided by ToolProvider.getSystemJavaCompiler(). Make sure the jdk.compiler module is available.");
        try {
            ClassFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));
            List<CharSequenceJavaFileObject> files = new ArrayList<>();
            files.add(new CharSequenceJavaFileObject(className, content));
            StringWriter out = new StringWriter();
            List<String> options = new ArrayList<>(compileOptions.options);
            if (!options.contains("-classpath")) {
                StringBuilder classpath = new StringBuilder();
                String separator = System.getProperty("path.separator");
                String prop = System.getProperty("java.class.path");
                if (prop != null && !"".equals(prop))
                    classpath.append(prop);
                if (cl instanceof URLClassLoader) {
                    for (URL url : ((URLClassLoader) cl).getURLs()) {
                        if (classpath.length() > 0)
                            classpath.append(separator);
                        if ("file".equals(url.getProtocol()))
                            classpath.append(new File(url.toURI()));
                    }
                }
                options.addAll(Arrays.asList("-classpath", classpath.toString()));
            }
            CompilationTask task = compiler.getTask(out, fileManager, null, options, null, files);
            if (!compileOptions.processors.isEmpty())
                task.setProcessors(compileOptions.processors);
            task.call();
            if (fileManager.isEmpty())
                throw new ReflectException("Compilation error: " + out);
            Class<?> result = null;
            // This works if we have private-access to the interfaces in the class hierarchy
            if (Reflect.CACHED_LOOKUP_CONSTRUCTOR != null) {
                result = fileManager.loadAndReturnMainClass(className, (name, bytes) -> Reflect.on(cl).call("defineClass", name, bytes, 0, bytes.length).get());
            } else /* [java-9] */
            // Lookup.defineClass() has only been introduced in Java 9. It is
            // required to get private-access to interfaces in the class hierarchy
            {
                // This method is called by client code from two levels up the current stack frame
                // We need a private-access lookup from the class in that stack frame in order to get
                // private-access to any local interfaces at that location.
                Class<?> caller = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).walk(s -> s.skip(2).findFirst().get().getDeclaringClass());
                // we can use the private-access Lookup of the caller class
                if (className.startsWith(caller.getPackageName() + ".") && // A better implementation is difficult at this point.
                Character.isUpperCase(className.charAt(caller.getPackageName().length() + 1))) {
                    Lookup privateLookup = MethodHandles.privateLookupIn(caller, lookup);
                    result = fileManager.loadAndReturnMainClass(className, (name, bytes) -> privateLookup.defineClass(bytes));
                } else // Otherwise, use an arbitrary class loader. This approach doesn't allow for
                // loading private-access interfaces in the compiled class's type hierarchy
                {
                    ByteArrayClassLoader c = new ByteArrayClassLoader(fileManager.classes());
                    result = fileManager.loadAndReturnMainClass(className, (name, bytes) -> c.loadClass(name));
                }
            }
            return result;
        } catch (ReflectException e) {
            throw e;
        } catch (Exception e) {
            throw new ReflectException("Error while compiling " + className, e);
        }
    }
}
Also used : OutputStream(java.io.OutputStream) SimpleJavaFileObject(javax.tools.SimpleJavaFileObject) RETAIN_CLASS_REFERENCE(java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE) java.util(java.util) ByteArrayOutputStream(java.io.ByteArrayOutputStream) JavaCompiler(javax.tools.JavaCompiler) JavaFileManager(javax.tools.JavaFileManager) URL(java.net.URL) StringWriter(java.io.StringWriter) MethodHandles(java.lang.invoke.MethodHandles) BiFunction(java.util.function.BiFunction) SimpleImmutableEntry(java.util.AbstractMap.SimpleImmutableEntry) Collectors(java.util.stream.Collectors) File(java.io.File) FileObject(javax.tools.FileObject) ForwardingJavaFileManager(javax.tools.ForwardingJavaFileManager) StandardJavaFileManager(javax.tools.StandardJavaFileManager) Lookup(java.lang.invoke.MethodHandles.Lookup) URLClassLoader(java.net.URLClassLoader) Stream(java.util.stream.Stream) CompilationTask(javax.tools.JavaCompiler.CompilationTask) Entry(java.util.Map.Entry) URI(java.net.URI) ToolProvider(javax.tools.ToolProvider) JavaCompiler(javax.tools.JavaCompiler) URL(java.net.URL) CompilationTask(javax.tools.JavaCompiler.CompilationTask) StringWriter(java.io.StringWriter) URLClassLoader(java.net.URLClassLoader) URLClassLoader(java.net.URLClassLoader) Lookup(java.lang.invoke.MethodHandles.Lookup) File(java.io.File)

Example 2 with RETAIN_CLASS_REFERENCE

use of java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE in project jOOQ by jOOQ.

the class Compile method compile.

static Class<?> compile(String className, String content, CompileOptions compileOptions) {
    Lookup lookup = MethodHandles.lookup();
    ClassLoader cl = lookup.lookupClass().getClassLoader();
    try {
        return cl.loadClass(className);
    } catch (ClassNotFoundException ignore) {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        try {
            ClassFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));
            List<CharSequenceJavaFileObject> files = new ArrayList<>();
            files.add(new CharSequenceJavaFileObject(className, content));
            StringWriter out = new StringWriter();
            List<String> options = new ArrayList<>(compileOptions.options);
            if (!options.contains("-classpath")) {
                StringBuilder classpath = new StringBuilder();
                String separator = System.getProperty("path.separator");
                String cp = System.getProperty("java.class.path");
                String mp = System.getProperty("jdk.module.path");
                if (cp != null && !"".equals(cp))
                    classpath.append(cp);
                if (mp != null && !"".equals(mp))
                    classpath.append(mp);
                if (cl instanceof URLClassLoader) {
                    for (URL url : ((URLClassLoader) cl).getURLs()) {
                        if (classpath.length() > 0)
                            classpath.append(separator);
                        if ("file".equals(url.getProtocol()))
                            classpath.append(new File(url.toURI()));
                    }
                }
                options.addAll(Arrays.asList("-classpath", classpath.toString()));
            }
            CompilationTask task = compiler.getTask(out, fileManager, null, options, null, files);
            if (!compileOptions.processors.isEmpty())
                task.setProcessors(compileOptions.processors);
            task.call();
            if (fileManager.isEmpty())
                throw new ReflectException("Compilation error: " + out);
            Class<?> result = null;
            if (Reflect.CACHED_LOOKUP_CONSTRUCTOR != null) {
                result = fileManager.loadAndReturnMainClass(className, (name, bytes) -> Reflect.on(cl).call("defineClass", name, bytes, 0, bytes.length).get());
            } else // Lookup.defineClass() has only been introduced in Java 9. It is
            // required to get private-access to interfaces in the class hierarchy
            {
                // This method is called by client code from two levels up the current stack frame
                // We need a private-access lookup from the class in that stack frame in order to get
                // private-access to any local interfaces at that location.
                Class<?> caller = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).walk(s -> s.skip(2).findFirst().get().getDeclaringClass());
                // we can use the private-access Lookup of the caller class
                if (className.startsWith(caller.getPackageName() + ".") && // A better implementation is difficult at this point.
                Character.isUpperCase(className.charAt(caller.getPackageName().length() + 1))) {
                    Lookup privateLookup = MethodHandles.privateLookupIn(caller, lookup);
                    result = fileManager.loadAndReturnMainClass(className, (name, bytes) -> privateLookup.defineClass(bytes));
                } else // Otherwise, use an arbitrary class loader. This approach doesn't allow for
                // loading private-access interfaces in the compiled class's type hierarchy
                {
                    ByteArrayClassLoader c = new ByteArrayClassLoader(fileManager.classes());
                    result = fileManager.loadAndReturnMainClass(className, (name, bytes) -> c.loadClass(name));
                }
            }
            return result;
        } catch (ReflectException e) {
            throw e;
        } catch (Exception e) {
            throw new ReflectException("Error while compiling " + className, e);
        }
    }
}
Also used : Arrays(java.util.Arrays) ByteArrayOutputStream(java.io.ByteArrayOutputStream) JavaFileManager(javax.tools.JavaFileManager) URL(java.net.URL) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) FileObject(javax.tools.FileObject) ForwardingJavaFileManager(javax.tools.ForwardingJavaFileManager) Lookup(java.lang.invoke.MethodHandles.Lookup) URLClassLoader(java.net.URLClassLoader) Charset(java.nio.charset.Charset) CompilationTask(javax.tools.JavaCompiler.CompilationTask) Map(java.util.Map) URI(java.net.URI) OutputStream(java.io.OutputStream) SimpleJavaFileObject(javax.tools.SimpleJavaFileObject) RETAIN_CLASS_REFERENCE(java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE) JavaCompiler(javax.tools.JavaCompiler) StringWriter(java.io.StringWriter) MethodHandles(java.lang.invoke.MethodHandles) File(java.io.File) StandardCharsets(java.nio.charset.StandardCharsets) StandardJavaFileManager(javax.tools.StandardJavaFileManager) List(java.util.List) Entry(java.util.Map.Entry) ToolProvider(javax.tools.ToolProvider) JavaCompiler(javax.tools.JavaCompiler) URL(java.net.URL) CompilationTask(javax.tools.JavaCompiler.CompilationTask) StringWriter(java.io.StringWriter) URLClassLoader(java.net.URLClassLoader) URLClassLoader(java.net.URLClassLoader) Lookup(java.lang.invoke.MethodHandles.Lookup) ArrayList(java.util.ArrayList) List(java.util.List) File(java.io.File)

Aggregations

ByteArrayOutputStream (java.io.ByteArrayOutputStream)2 File (java.io.File)2 OutputStream (java.io.OutputStream)2 StringWriter (java.io.StringWriter)2 RETAIN_CLASS_REFERENCE (java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE)2 MethodHandles (java.lang.invoke.MethodHandles)2 Lookup (java.lang.invoke.MethodHandles.Lookup)2 URI (java.net.URI)2 URL (java.net.URL)2 URLClassLoader (java.net.URLClassLoader)2 Entry (java.util.Map.Entry)2 FileObject (javax.tools.FileObject)2 ForwardingJavaFileManager (javax.tools.ForwardingJavaFileManager)2 JavaCompiler (javax.tools.JavaCompiler)2 CompilationTask (javax.tools.JavaCompiler.CompilationTask)2 JavaFileManager (javax.tools.JavaFileManager)2 SimpleJavaFileObject (javax.tools.SimpleJavaFileObject)2 StandardJavaFileManager (javax.tools.StandardJavaFileManager)2 ToolProvider (javax.tools.ToolProvider)2 Charset (java.nio.charset.Charset)1