From f3bb007d8d4efb82b62b83c00c0a598b18939281 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sat, 1 Mar 2025 12:00:31 -0500 Subject: [PATCH 1/7] * add info helper * add filter-externs helper * eval comments - the problem is complex type expressions --- src/main/clojure/cljs/externs.clj | 18 ++++++++++++++++++ src/test/clojure/cljs/externs_infer_tests.clj | 15 +++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index d86aa91ef..a041b6d6f 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -313,6 +313,24 @@ (parse-externs (resource->source-file rsrc)) (:module desc))})))) +(defn info + "Helper for grabbing var info from an externs map. + Example: + (info externs '[Number isNaN]) + See `externs-map`" + [externs props] + (-> externs + (get-in (butlast props)) + (find (last props)) + first meta)) + +(defn filtered-externs [f] + (->> + (filter + #(= f (.getName %)) + (default-externs)) + first parse-externs index-externs)) + (comment (require '[clojure.java.io :as io] '[cljs.closure :as closure] diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 8ca7ff9aa..a5b2ce06a 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -486,6 +486,21 @@ :with-core? true})] (is (empty? @ws))))) +(comment + + (externs/info + (::ana/externs @(env/default-compiler-env)) + '[console]) + + (externs/info + (::ana/externs @(env/default-compiler-env)) + '[crypto]) + + (-> (externs/filtered-externs "externs.zip//w3c_webcrypto.js") + (externs/info '[crypto])) + + ) + (comment (binding [ana/*cljs-ns* ana/*cljs-ns*] (ana/no-warn From ef98670becd32c766762cef3adc6f5f18a90159d Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sat, 1 Mar 2025 12:25:22 -0500 Subject: [PATCH 2/7] * type parsing wip --- src/main/clojure/cljs/externs.clj | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index a041b6d6f..7b49ae8c3 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -38,6 +38,22 @@ (into [] (butlast props)) (with-meta (last props) ty)))) +(def token->kw + {Token/BANG :bang + Token/BLOCK :block + Token/PIPE :pipe + Token/STRINGLIT :string-lit}) + +(defn parse-texpr [^Node root] + (let [token (get token->kw (.getToken root)) + children (.children root)] + (merge + {:type token} + (when-not (empty? children) + {:children (vec (map parse-texpr (.children root)))}) + (when (= :string-lit token) + {:value (.getString root)})))) + (defn get-tag [^JSTypeExpression texpr] (when-let [root (.getRoot texpr)] (if (.isString root) From 0f54664010cda86957aa34ee9990bbbb852f3336 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 3 Mar 2025 11:16:38 -0500 Subject: [PATCH 3/7] - add type simplifier --- src/main/clojure/cljs/externs.clj | 48 ++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 7b49ae8c3..c5760051b 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -42,25 +42,39 @@ {Token/BANG :bang Token/BLOCK :block Token/PIPE :pipe - Token/STRINGLIT :string-lit}) + Token/STRINGLIT :string-lit + Token/QMARK :qmark + Token/STAR :star}) (defn parse-texpr [^Node root] - (let [token (get token->kw (.getToken root)) - children (.children root)] - (merge - {:type token} - (when-not (empty? children) - {:children (vec (map parse-texpr (.children root)))}) - (when (= :string-lit token) - {:value (.getString root)})))) + (when-let [token (get token->kw (.getToken root))] + (let [children (.children root)] + (merge + {:type token} + (when-not (empty? children) + {:children (vec (map parse-texpr (.children root)))}) + (when (= :string-lit token) + {:value (.getString root)}))))) + +(defn undefined? + [{:keys [type value] :as texpr}] + (and (= type :string-lit) + (= "undefined" value))) + +(defn simplify-texpr + [texpr] + (case (:type texpr) + :string-lit (some-> (:value texpr) symbol) + (:star :qmark) 'any + :bang (simplify-texpr (-> texpr :children first)) + :pipe (let [[x y] (:children texpr)] + (if (undefined? y) + (simplify-texpr x) + 'any)) + 'any)) (defn get-tag [^JSTypeExpression texpr] - (when-let [root (.getRoot texpr)] - (if (.isString root) - (symbol (.getString root)) - (if-let [child (.. root getFirstChild)] - (if (.isString child) - (symbol (.. child getString))))))) + (some-> (.getRoot texpr) parse-texpr simplify-texpr)) (defn params->method-params [xs] (letfn [(not-opt? [x] @@ -172,7 +186,7 @@ [lhs]) [])))) -(defmethod parse-extern-node Token/GETPROP [node] +(defmethod parse-extern-node Token/GETPROP [^Node node] (when-not *ignore-var* (let [props (map symbol (string/split (.getQualifiedName node) #"\."))] [(if-let [ty (get-var-info node)] @@ -181,7 +195,7 @@ ;; JavaScript Object literal ;; { ... } -(defmethod parse-extern-node Token/OBJECTLIT [node] +(defmethod parse-extern-node Token/OBJECTLIT [^Node node] (when (> (.getChildCount node) 0) (loop [nodes (.children node) externs []] From 6e927aa46333f5b7ca1bfd7f79308b2f3a627ab7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 4 Mar 2025 10:12:38 -0500 Subject: [PATCH 4/7] * convert eval comments into tests --- src/test/clojure/cljs/externs_infer_tests.clj | 15 --------------- src/test/clojure/cljs/externs_parsing_tests.clj | 13 ++++++++++++- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index a5b2ce06a..8ca7ff9aa 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -486,21 +486,6 @@ :with-core? true})] (is (empty? @ws))))) -(comment - - (externs/info - (::ana/externs @(env/default-compiler-env)) - '[console]) - - (externs/info - (::ana/externs @(env/default-compiler-env)) - '[crypto]) - - (-> (externs/filtered-externs "externs.zip//w3c_webcrypto.js") - (externs/info '[crypto])) - - ) - (comment (binding [ana/*cljs-ns* ana/*cljs-ns*] (ana/no-warn diff --git a/src/test/clojure/cljs/externs_parsing_tests.clj b/src/test/clojure/cljs/externs_parsing_tests.clj index effad773d..f52e580dd 100644 --- a/src/test/clojure/cljs/externs_parsing_tests.clj +++ b/src/test/clojure/cljs/externs_parsing_tests.clj @@ -8,9 +8,11 @@ (ns cljs.externs-parsing-tests (:require [cljs.closure :as closure] + [cljs.analyzer :as ana] + [cljs.env :as env] [cljs.externs :as externs] [clojure.java.io :as io] - [clojure.test :as test :refer [deftest is]]) + [clojure.test :as test :refer [deftest is testing]]) (:import [com.google.javascript.jscomp CommandLineRunner])) (deftest cljs-3121 @@ -45,6 +47,15 @@ (find 'HTMLDocument) first meta)] (is (= 'Document (:super info))))) +(deftest test-parse-closure-type-annotations + (let [externs (::ana/externs @(env/default-compiler-env))] + (testing "JS global console has tag Console" + (let [info (externs/info externs '[console])] + (is (= 'Console (:tag info))))) + (testing "JS global crypto has tag webCrypto.Crypto" + (let [info (externs/info externs '[crypto])] + (is (= 'webCrypto.Crypto (:tag info))))))) + (comment (externs/parse-externs From 300315cc1fca5e9f3b0d4426d8964c15738bdd5e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 19 Mar 2025 11:02:35 -0400 Subject: [PATCH 5/7] * more assertions --- .../clojure/cljs/externs_parsing_tests.clj | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/test/clojure/cljs/externs_parsing_tests.clj b/src/test/clojure/cljs/externs_parsing_tests.clj index f52e580dd..98f9b90de 100644 --- a/src/test/clojure/cljs/externs_parsing_tests.clj +++ b/src/test/clojure/cljs/externs_parsing_tests.clj @@ -52,12 +52,27 @@ (testing "JS global console has tag Console" (let [info (externs/info externs '[console])] (is (= 'Console (:tag info))))) - (testing "JS global crypto has tag webCrypto.Crypto" + (testing "JS global crypto has tag webCrypto.Crypto from: + @type {!webCrypto.Crypto|undefined}" (let [info (externs/info externs '[crypto])] - (is (= 'webCrypto.Crypto (:tag info))))))) + (is (= 'webCrypto.Crypto (:tag info))))) + (testing "Generic return type on crypto methods returns ClojureScript relevant + type info:" + (testing "@return {!Promise}" + (let [info (externs/info externs '[webCrypto SubtleCrypto prototype encrypt])] + (is (= 'Promise (:ret-tag info))))) + (testing "@return {!Promise}" + (let [info (externs/info externs '[webCrypto SubtleCrypto prototype deriveKey])] + (is (= 'Promise (:ret-tag info))))) + (testing "@return {!Int8Array|!Uint8Array|!Uint8ClampedArray|!Int16Array|!Uint16Array|!Int32Array|!Uint32Array|!BigInt64Array|!BigUint64Array}" + (let [info (externs/info externs '[webCrypto SubtleCrypto prototype getRandomValues])] + (is (= 'any (:ret-tag info)))))))) (comment + (let [externs (::ana/externs @(env/default-compiler-env))] + (externs/info externs '[webCrypto Crypto prototype getRandomValues])) + (externs/parse-externs (externs/resource->source-file (io/resource "goog/object/object.js"))) From ae02f6ff26f987a9ca737d52d8ee394a94343f3a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 19 Mar 2025 11:08:47 -0400 Subject: [PATCH 6/7] * cleanup some old reflection warnings --- src/main/clojure/cljs/externs.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index c5760051b..910643b49 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -245,8 +245,8 @@ (loop [nodes (cond-> nodes ;; handle goog.modules which won't have top-levels ;; need to look at internal children - (= Token/MODULE_BODY (some-> nodes first .getToken)) - (-> first .children)) + (= Token/MODULE_BODY (some-> nodes ^Node (first) .getToken)) + (-> ^Node (first) .children)) externs []] (if (empty? nodes) externs From e160523bdb34685c0a1f71098e66b5ee674a6a12 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 19 Mar 2025 11:18:17 -0400 Subject: [PATCH 7/7] * typo --- src/test/clojure/cljs/externs_parsing_tests.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/externs_parsing_tests.clj b/src/test/clojure/cljs/externs_parsing_tests.clj index 98f9b90de..ed0cfdb70 100644 --- a/src/test/clojure/cljs/externs_parsing_tests.clj +++ b/src/test/clojure/cljs/externs_parsing_tests.clj @@ -65,7 +65,7 @@ (let [info (externs/info externs '[webCrypto SubtleCrypto prototype deriveKey])] (is (= 'Promise (:ret-tag info))))) (testing "@return {!Int8Array|!Uint8Array|!Uint8ClampedArray|!Int16Array|!Uint16Array|!Int32Array|!Uint32Array|!BigInt64Array|!BigUint64Array}" - (let [info (externs/info externs '[webCrypto SubtleCrypto prototype getRandomValues])] + (let [info (externs/info externs '[webCrypto Crypto prototype getRandomValues])] (is (= 'any (:ret-tag info)))))))) (comment