diff --git a/src/clj_rhino.clj b/src/clj_rhino.clj index dbfa3a3..f2efa5c 100644 --- a/src/clj_rhino.clj +++ b/src/clj_rhino.clj @@ -1,14 +1,22 @@ (ns clj-rhino (:refer-clojure :exclude (eval get get-in set!)) - (:import [org.mozilla.javascript Context UniqueTag])) + (:import [org.mozilla.javascript Context UniqueTag NativeArray NativeObject])) (defprotocol RhinoConvertible (-to-rhino [object scope ctx] "convert a value to a rhino compatible type")) +(defprotocol ClojureConvertible + (-from-rhino [object] + "convert a value from rhino to a more clojure friendly representation")) + (defn to-js [obj scope ctx] "convert obj to a rhino compatible object" (-to-rhino obj scope ctx)) +(defn from-js [obj] + "convert obj from rhino into a clojure friendly representation" + (-from-rhino obj)) + (defn- return-self [obj scope ctx] obj) (defn to-js-array [arr scope ctx] @@ -52,6 +60,31 @@ ;; Maybe a Java array, otherwise fail (extend java.lang.Object RhinoConvertible {:-to-rhino to-js-generic}) +(defn- entryset-to-pair [entry] + (let [str-key (.getKey entry) + key (keyword str-key) + js-value (.getValue entry) + val (from-js js-value)] + + [key val])) + +(defn- from-js-object [obj] + (apply hash-map (mapcat entryset-to-pair (.entrySet obj)))) + +(extend nil ClojureConvertible {:-from-rhino identity}) +(extend java.lang.Boolean ClojureConvertible {:-from-rhino identity}) +(extend java.lang.Number ClojureConvertible {:-from-rhino identity}) +(extend java.math.BigInteger ClojureConvertible {:-from-rhino identity}) +(extend java.math.BigDecimal ClojureConvertible {:-from-rhino identity}) +(extend java.lang.CharSequence ClojureConvertible {:-from-rhino identity}) +(extend java.lang.Object ClojureConvertible {:-from-rhino identity}) +; NOTE: undefined and null will return nil, there are other tags which should not +; be produced from a js program +; https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/UniqueTag.java +(extend UniqueTag ClojureConvertible {:-from-rhino (fn [obj] nil)}) +(extend NativeObject ClojureConvertible {:-from-rhino from-js-object}) +(extend NativeArray ClojureConvertible {:-from-rhino (comp vec (partial map from-js))}) + (def insecure-vars ["isXMLName" "uneval" "InternalError" "JavaException" "With" "Call" "Script" "Iterator" "StopIteration", "Packages" "java" "javax" "org" "com" "edu" "net" diff --git a/test/clj_rhino/core_test.clj b/test/clj_rhino/core_test.clj index 3364e89..ffeb3bb 100644 --- a/test/clj_rhino/core_test.clj +++ b/test/clj_rhino/core_test.clj @@ -16,6 +16,12 @@ (is (= (js/to-js item scope ctx) js-item))) (map vector arr js-arr))))) +(defn- is-identity [value] + (is (= (js/from-js value) value))) + +(defn- assert-simetric-convertion [obj1 scope ctx obj2] + (is (= (js/from-js (js/to-js obj1 scope ctx)) obj2))) + (deftest js-test (testing "undefined? works" (is (not (js/undefined? 1))) @@ -114,4 +120,39 @@ (js/with-context (fn [ctx] (let [scope (js/new-safe-scope)] (is (thrown? Exception (js/to-js (atom {}) scope ctx))))))) + + (testing "from-js works" + (is-identity nil) + (is-identity 1) + (is-identity 1.2) + (is-identity "asd") + (is-identity true) + + (js/with-context + (fn [ctx] + (let [scope (js/new-safe-scope)] + (is (= (js/from-js UniqueTag/NOT_FOUND) nil)) + (assert-simetric-convertion [] scope ctx []) + (assert-simetric-convertion [1] scope ctx [1]) + (assert-simetric-convertion [1 nil] scope ctx [1 nil]) + (assert-simetric-convertion [1 nil 1.2] scope ctx [1 nil 1.2]) + (assert-simetric-convertion [1 nil 1.2 "asd"] scope ctx + [1 nil 1.2 "asd"]) + (assert-simetric-convertion [1 nil 1.2 "asd" true] scope ctx + [1 nil 1.2 "asd" true]) + + (assert-simetric-convertion {} scope ctx {}) + (assert-simetric-convertion {:b 1} scope ctx {:b 1}) + (assert-simetric-convertion {:b 1 "c" true} scope ctx + {:b 1 :c true}) + (assert-simetric-convertion {:b 1 "c" true :foo nil} scope ctx + {:b 1 :c true :foo nil}) + (assert-simetric-convertion {:b {:c {:d 4}}} scope ctx + {:b {:c {:d 4}}}) + (assert-simetric-convertion {:b {"c" {:d 4}}} scope ctx + {:b {:c {:d 4}}}) + (assert-simetric-convertion {:b {"c" {:d [{"e" 4}]}}} scope ctx + {:b {:c {:d [{:e 4}]}}}) + )))) ) +