Last active
April 26, 2019 06:47
-
-
Save cgrand/259fa9ee3553d642850501ed431fc74d to your computer and use it in GitHub Desktop.
For toolsmithes really caring about isolation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; Clojure 1.6.0 | |
=> *clojure-version* | |
{:major 1, :minor 6, :incremental 0, :qualifier nil} | |
=> (def env1 (create-clojure-env)) | |
#'net.cgrand.quarantine/env1 | |
=> (env1 *in* *out*) | |
clojure.core=> *clojure-version* | |
{:major 1, :minor 8, :incremental 0, :qualifier nil} | |
clojure.core=> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defproject net.cgrand/quarantine "0.1.0-SNAPSHOT" | |
:description "Clojure in a function." | |
:license {:name "Eclipse Public License" | |
:url "http://www.eclipse.org/legal/epl-v10.html"} | |
:plugins [[com.pupeno/jar-copier "0.4.0"]] | |
:prep-tasks ["javac" "compile" "jar-copier"] | |
:jar-copier {:jars [[org.clojure/clojure "1.8.0"]] | |
:destination "resources/jars"} | |
:dependencies [[org.clojure/clojure "1.6.0"]]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns net.cgrand.quarantine) | |
(defn jar-url [] | |
(with-open [in (.getResourceAsStream (or (.getContextClassLoader (Thread/currentThread)) | |
(ClassLoader/getSystemClassLoader)) | |
"jars/org.clojure/clojure.jar")] | |
(let [bbuf (byte-array (* 4 1024 1024)) | |
tmpfile (java.io.File/createTempFile "isolation-" ".jar")] | |
(with-open [out (java.io.FileOutputStream. tmpfile)] | |
(loop [] | |
(let [got (.read in bbuf)] | |
(when (<= 0 got) | |
(.write out bbuf 0 got))))) | |
(-> tmpfile .toURI .toURL)))) | |
(defn create-clojure-env | |
"Returns a clojure env. A clojure env is a function of two arguments: in and out. | |
When it is called it starts a REPL on these streams. | |
All REPL started from the same Clojure env share the same namespaces. | |
Namespaces are isolated between envs. | |
Envs are sol isolated that they wouldn't recognize a keyword from another env." | |
[] | |
(let [cl (java.net.URLClassLoader. (into-array [(jar-url)]) nil) | |
in-quarantine | |
(fn [stub] | |
(let [ccl (.getContextClassLoader (Thread/currentThread))] | |
(.setContextClassLoader (Thread/currentThread) cl) | |
(try | |
(stub) | |
(finally | |
(.setContextClassLoader (Thread/currentThread) ccl))))) | |
clj (in-quarantine #(Class/forName "clojure.java.api.Clojure" true cl)) | |
IFn (Class/forName "clojure.lang.IFn" true cl) | |
varm (.getMethod clj "var" (into-array [Object])) | |
readm (.getMethod clj "read" (into-array [String])) | |
evalv (.invoke varm clj (to-array ["clojure.core/eval"])) | |
invoke1 (.getMethod IFn "invoke" (into-array [Object])) | |
invoke2 (.getMethod IFn "invoke" (into-array [Object Object])) | |
eval (fn [x] | |
(in-quarantine | |
#(let [form (.invoke readm clj (to-array [(pr-str x)]))] | |
(.invoke invoke1 evalv (to-array [form]))))) | |
repl (eval '(fn [in out] | |
(binding [*in* (clojure.lang.LineNumberingPushbackReader. in) | |
*out* out] | |
(clojure.main/repl))))] | |
(fn [in out] | |
(in-quarantine | |
#(.invoke invoke2 repl (to-array [in out])))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment