diff --git a/src/sci/impl/analyzer.cljc b/src/sci/impl/analyzer.cljc index c81ed700..384e40c2 100644 --- a/src/sci/impl/analyzer.cljc +++ b/src/sci/impl/analyzer.cljc @@ -1226,21 +1226,35 @@ :file @utils/current-file) nil)) (throw-error-with-location (str "Unable to resolve classname: " class-sym) class-sym))) - :clj (if-let [class (:class (interop/resolve-class-opts ctx class-sym))] - (invoke-constructor-node ctx class args) - (if-let [record (records/resolve-record-class ctx class-sym)] - (let [args (analyze-children ctx args)] - ;; _ctx expr f analyzed-children stack - (return-call ctx - ;; for backwards compatibility with error reporting - expr - (:sci.impl/constructor (meta record)) - args - (assoc (meta expr) - :ns @utils/current-ns - :file @utils/current-file) - nil)) - (throw-error-with-location (str "Unable to resolve classname: " class-sym) class-sym))) + :clj (let [class-opts (interop/resolve-class-opts ctx class-sym) + ctor (:constructor class-opts)] + (case (interop/member-disposition ctor class-opts nil) + :override (let [args (analyze-children ctx args)] + (return-call ctx + expr + ctor + args + (assoc (meta expr) + :ns @utils/current-ns + :file @utils/current-file) + nil)) + :deny (utils/throw-error-with-location + (str "Constructor of " class-sym " not allowed!") expr) + :reflect (if-let [class (:class class-opts)] + (invoke-constructor-node ctx class args) + (if-let [record (records/resolve-record-class ctx class-sym)] + (let [args (analyze-children ctx args)] + ;; _ctx expr f analyzed-children stack + (return-call ctx + ;; for backwards compatibility with error reporting + expr + (:sci.impl/constructor (meta record)) + args + (assoc (meta expr) + :ns @utils/current-ns + :file @utils/current-file) + nil)) + (throw-error-with-location (str "Unable to resolve classname: " class-sym) class-sym))))) :cljs (if (symbol? class-sym) ;; try to statically analyze class for better performance (if-let [class (or diff --git a/test/sci/interop_test.cljc b/test/sci/interop_test.cljc index 423a21d5..cd7b9f11 100644 --- a/test/sci/interop_test.cljc +++ b/test/sci/interop_test.cljc @@ -88,7 +88,7 @@ :instance-fields {:closed true 'x true}}}}))) (is (thrown-with-msg? Exception #"Field x on .*PublicFields not allowed" (sci/eval-string "(.-x (PublicFields.))" - {:classes {'PublicFields {:class PublicFields :closed true}}}))) + {:classes {'PublicFields {:class PublicFields :closed true :constructor true}}}))) (is (thrown-with-msg? Exception #"Field x on .*PublicFields not allowed" (sci/eval-string "(.-x (PublicFields.))" {:classes {'PublicFields {:class PublicFields @@ -152,7 +152,35 @@ #?(:clj (deftest constructor-test (is (= "dude" (eval* "(String. (str \"dude\"))"))) - (is (= "dude" (eval* "(new String (str \"dude\"))"))))) + (is (= "dude" (eval* "(new String (str \"dude\"))"))) + + ;; Manipulate constructors by configuring other classes and functions. + ;; Don't test in native because the config is not correctly serialized in this case + (when-not tu/native? + (is (instance? java.io.File (tu/eval* "(String. \"dude\")" {:classes {'String {:class java.io.File}}}))) + + (is (= 123 (tu/eval* "(String. \"dude\")" {:classes {'String {:constructor (fn [_] 123)}}}))) + (is (= 123 (tu/eval* "(String. \"dude\")" {:classes {'String {:class String + :constructor (fn [_] 123)}}}))) + + ;; Constructors and :closed classes + (is (thrown-with-msg? Exception #"Constructor of String not allowed" (tu/eval* "(String. \"dude\")" {:classes {'String {:class String + :closed true}}}))) + + (is (= "dude" (tu/eval* "(String. \"dude\")" {:classes {'String {:class String + :closed true + :constructor true}}}))) + (is (= 123 (tu/eval* "(String. \"dude\")" {:classes {'String {:class String + :closed true + :constructor (fn [_] 123)}}}))) + ;; If you *just* want to deny a constructor you have to override it with a function that throws + (let [deny-ctor-opts {:classes {'String {:class String + :constructor (fn [_] (throw (ex-info "Constructor of String not allowed" {})))}}}] + (is (thrown-with-msg? Exception #"Constructor of String not allowed" (tu/eval* "(String. \"dude\")" deny-ctor-opts))) + (is (= 4 (tu/eval* "(.length \"dude\")" deny-ctor-opts)))) + + + ))) #?(:clj (deftest import-test