From 19677dbcef4702c65b1c1f584800dd56400c1114 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Mon, 27 Jan 2025 21:23:37 -0500 Subject: [PATCH] * add js-global? helper to analyzer * in host call check for this case - if js global change the type resolution via externs * add some comment eval exprs for context --- src/main/clojure/cljs/analyzer.cljc | 19 ++++++++++---- src/main/clojure/cljs/compiler.cljc | 2 +- src/main/clojure/cljs/externs.clj | 1 + .../clojure/cljs/externs_parsing_tests.clj | 25 ++++++++++++++++--- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 8c61c4586..ba8ff4240 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3542,6 +3542,12 @@ (list* '. dot-form) " with classification " (classify-dot-form dot-form)))))) +(defn js-global? + "Return true if the expr is a JS global" + [expr] + (and (= 'js (:ns expr)) + (some? (get-in (get-externs) [(-> (:name expr) name symbol)])))) + (defn analyze-dot [env target field member+ form] (let [v [target field member+] {:keys [dot-action target method field args]} (build-dot-form v) @@ -3550,11 +3556,14 @@ form-meta (meta form) target-tag (:tag targetexpr) prop (or field method) - tag (or (:tag form-meta) - (and (js-tag? target-tag) - (vary-meta (normalize-js-tag target-tag) - update-in [:prefix] (fnil conj '[Object]) prop)) - nil)] + tag (if (js-global? targetexpr) + ;; we have a known global, don't treat as instance of some type + (with-meta 'js {:prefix [(-> targetexpr :name name symbol) prop]}) + (or (:tag form-meta) + (and (js-tag? target-tag) + (vary-meta (normalize-js-tag target-tag) + update-in [:prefix] (fnil conj '[Object]) prop)) + nil))] (when (and (not= 'constructor prop) (not (string/starts-with? (str prop) "cljs$")) (not (-> prop meta :protocol-prop))) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index b96c09b36..632cc75de 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -641,7 +641,7 @@ (defn safe-test? [env e] (let [tag (ana/infer-tag env e)] - (or (#{'boolean 'seq} tag) (truthy-constant? e)))) + (or ('#{boolean seq js/Boolean} tag) (truthy-constant? e)))) (defmethod emit* :if [{:keys [test then else env unchecked]}] diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 07ab13c86..71619e25c 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -432,5 +432,6 @@ (get 'React) (find 'Component) first meta) + (info (externs-map) '[Number]) (-> (info (externs-map) '[Number isNaN]) :ret-tag) ;; => boolean ) diff --git a/src/test/clojure/cljs/externs_parsing_tests.clj b/src/test/clojure/cljs/externs_parsing_tests.clj index a698bf866..1d5a51698 100644 --- a/src/test/clojure/cljs/externs_parsing_tests.clj +++ b/src/test/clojure/cljs/externs_parsing_tests.clj @@ -48,16 +48,35 @@ (find 'HTMLDocument) first meta)] (is (= 'Document (:super info))))) -;; TODO: -;; analyze (.isNaN js/NaN 1) -;; node :tag should be js/Boolean +(deftest test-number-infer-test + (let [cenv (env/default-compiler-env) + aenv (ana/empty-env)] + (is (= (env/with-compiler-env cenv + (:tag (ana/analyze aenv '(.isNaN js/Number 1)))) + 'js/Boolean)))) + +;; TODO: js/subtle.crypto (comment + (externs/info + (::ana/externs @(env/default-compiler-env)) + '[Number]) + + (externs/info + (::ana/externs @(env/default-compiler-env)) + '[Number isNaN]) + ;; js/Boolean (env/with-compiler-env (env/default-compiler-env) (ana/js-tag '[Number isNaN] :ret-tag)) + ;; js + (let [cenv (env/default-compiler-env) + aenv (ana/empty-env)] + (->> (env/with-compiler-env cenv + (:tag (ana/analyze aenv '(.isNaN js/Number 1)))))) + (externs/parse-externs (externs/resource->source-file (io/resource "goog/object/object.js")))