diff --git a/src/sci/impl/evaluator.cljc b/src/sci/impl/evaluator.cljc index f51389f8..d694ca14 100644 --- a/src/sci/impl/evaluator.cljc +++ b/src/sci/impl/evaluator.cljc @@ -140,31 +140,40 @@ (if-not (identical? none-sentinel v) v (let [instance-class #?(:clj (or (when tag-class - (if (instance? tag-class instance-expr*) - tag-class - (class instance-expr*))) - (class instance-expr*)) + (if (instance? tag-class instance-expr*) + tag-class + (class instance-expr*))) + (class instance-expr*)) :cljs (type instance-expr*)) env @(:env ctx) class->opts (:class->opts env) allowed? (or #?(:cljs allowed) - (get class->opts :allow) - (let [instance-class-name #?(:clj (.getName ^Class instance-class) - :cljs (.-name instance-class)) - instance-class-symbol (symbol instance-class-name)] - (get class->opts instance-class-symbol))) - ^Class target-class (if allowed? instance-class - (when-let [f (:public-class env)] - (f instance-expr*)))] - ;; we have to check options at run time, since we don't know what the class - ;; of instance-expr is at analysis time - (when-not #?(:clj target-class - :cljs allowed?) - (throw-error-with-location (str "Method " method-str " on " instance-class " not allowed!") instance-expr)) - (if field-access - (interop/invoke-instance-field instance-expr* target-class method-str) - (interop/invoke-instance-method ctx bindings instance-expr* target-class method-str args arg-count arg-types)))))) + (get class->opts :allow))] + (if allowed? + (if field-access + (interop/invoke-instance-field instance-expr* instance-class method-str) + (interop/invoke-instance-method ctx bindings instance-expr* instance-class method-str args arg-count arg-types)) + (let [instance-class-name #?(:clj (.getName ^Class instance-class) + :cljs (.-name instance-class)) + instance-class-symbol (symbol instance-class-name)] + (if-let [class-config (get class->opts instance-class-symbol)] + (if-let [f (some-> class-config :instance-methods + (get (symbol method-str)))] + (apply f instance-expr* (map (fn [arg] (sci.impl.types/eval arg ctx bindings)) args)) + (if field-access + (interop/invoke-instance-field instance-expr* instance-class method-str) + (interop/invoke-instance-method ctx bindings instance-expr* instance-class method-str args arg-count arg-types))) + (let [^Class target-class (when-let [f (:public-class env)] + (f instance-expr*))] + + ;; we have to check options at run time, since we don't know what the class + ;; of instance-expr is at analysis time + (if target-class + (if field-access + (interop/invoke-instance-field instance-expr* target-class method-str) + (interop/invoke-instance-method ctx bindings instance-expr* target-class method-str args arg-count arg-types)) + (throw-error-with-location (str "Method " method-str " on " instance-class " not allowed!") instance-expr)))))))))) ;;;; End interop diff --git a/test/sci/interop_test.cljc b/test/sci/interop_test.cljc index c41701de..af5d797b 100644 --- a/test/sci/interop_test.cljc +++ b/test/sci/interop_test.cljc @@ -42,7 +42,19 @@ (is (= #{:a} (tu/eval* "(.keySet {:a 1})" {:classes {'java.util.Map 'java.util.Map :public-class (fn [o] - (when (instance? java.util.Map o) java.util.Map))}}))))))) + (when (instance? java.util.Map o) java.util.Map))}}))))) + (testing "single method override" + (let [config {:classes {'java.lang.String + {:class java.lang.String + :instance-methods {'lastIndexOf (fn [s needle] (.lastIndexOf s needle)) ; String/.lastIndexOf syntax only added as of 1.12 + 'toString + ;; REVIEW should toString also receive the class like the functions in :static-methods + (fn [_s] + :dude)}}}}] + (is (= :dude (sci/eval-string "(.toString \"your name\")" config))) + (is (= 9 (sci/eval-string "(.length \"your name\")" config))) + (is (= 5 (sci/eval-string "(let [needle \"name\"] (.lastIndexOf \"your name\" needle))" config))))))) + #?(:clj (deftest instance-fields