【问题标题】:How to debug a NullPointerException from lein run?如何从 lein run 调试 NullPointerException?
【发布时间】:2021-03-11 16:05:21
【问题描述】:

我正在尝试创建一个简单的 Clojure 应用程序来解析标准输入并相应地执行一些命令。我用lein new app myproject 创建了一个新的leiningen 项目并将我的代码添加到src/myproject/core.clj

(ns myproject.core
  (:require [clojure.string :as str])
  (:gen-class))

(def global-vector (ref [{:name "Bob" :age 22} {:name "Alice" :age 21}]))

(defn call [fn_name & fn_args]
  (apply (resolve (symbol fn_name)) (map keyword fn_args)))

(defn select
  [key]
  (dosync
    (map #(get % key) @global-vector)))

(defn -main []
  (while true
    (let [[command cargs] (str/split (read-line) #" ")]
    (println (call command cargs)))))

这里要明确的是重现我正在尝试做的事情的步骤:

  • 我想从命令行运行这个程序(目前我使用lein run
  • 它应该等待用户输入(我使用read-line函数)
  • 我输入一些文字:select name
  • 我希望程序打印存储在 global-vector 中的名称,但它会因 NPE 而崩溃

使用lein repl 运行时,一切似乎都按预期工作:

myproject.core=> (select :name)
("Bob" "Alice")
myproject.core=> (apply select [:name])
("Bob" "Alice")
myproject.core=> (call "select" "name")
("Bob" "Alice")
myproject.core=> (let [[a b] (str/split "select name" #" ")]
        #_=>     (println (call a b)))
(Bob Alice)
nil
myproject.core=> (-main)
select name
(Bob Alice)

但是当我用lein run 运行它并输入一些输入时,我得到一个NullPointerException

$lein run
select name
Syntax error (NullPointerException) compiling at (/private/var/folders/lt/z56f492j3f3gcdf83y4f8x2r0000gn/T/form-init2363625704485841211.clj:1:125).
null

Full report at:
/var/folders/lt/z56f492j3f3gcdf83y4f8x2r0000gn/T/clojure-6381054570172229346.edn

报告文件内容如下:

{:clojure.main/message
 "Syntax error (NullPointerException) compiling at (/private/var/folders/lt/z56f492j3f3gcdf83y4f8x2r0000gn/T/form-init2363625704485841211.clj:1:125).\nnull\n",
 :clojure.main/triage
 {:clojure.error/phase :compile-syntax-check,
  :clojure.error/line 1,
  :clojure.error/column 125,
  :clojure.error/source "form-init2363625704485841211.clj",
  :clojure.error/path
  "/private/var/folders/lt/z56f492j3f3gcdf83y4f8x2r0000gn/T/form-init2363625704485841211.clj",
  :clojure.error/class java.lang.NullPointerException},
 :clojure.main/trace
 {:via
  [{:type clojure.lang.Compiler$CompilerException,
    :message
    "Syntax error compiling at (/private/var/folders/lt/z56f492j3f3gcdf83y4f8x2r0000gn/T/form-init2363625704485841211.clj:1:125).",
    :data
    {:clojure.error/phase :compile-syntax-check,
     :clojure.error/line 1,
     :clojure.error/column 125,
     :clojure.error/source
     "/private/var/folders/lt/z56f492j3f3gcdf83y4f8x2r0000gn/T/form-init2363625704485841211.clj"},
    :at [clojure.lang.Compiler load "Compiler.java" 7648]}
   {:type java.lang.NullPointerException,
    :at [clojure.core$apply invokeStatic "core.clj" 665]}],
  :trace
  [[clojure.core$apply invokeStatic "core.clj" 665]
   [clojure.core$apply invoke "core.clj" 660]
   [myproject.core$call invokeStatic "core.clj" 8]
   [myproject.core$call doInvoke "core.clj" 7]
   [clojure.lang.RestFn invoke "RestFn.java" 423]
   [myproject.core$_main invokeStatic "core.clj" 18]
   [myproject.core$_main invoke "core.clj" 15]
   [clojure.lang.Var invoke "Var.java" 380]
   [user$eval140 invokeStatic "form-init2363625704485841211.clj" 1]
   [user$eval140 invoke "form-init2363625704485841211.clj" 1]
   [clojure.lang.Compiler eval "Compiler.java" 7177]
   [clojure.lang.Compiler eval "Compiler.java" 7167]
   [clojure.lang.Compiler load "Compiler.java" 7636]
   [clojure.lang.Compiler loadFile "Compiler.java" 7574]
   [clojure.main$load_script invokeStatic "main.clj" 475]
   [clojure.main$init_opt invokeStatic "main.clj" 477]
   [clojure.main$init_opt invoke "main.clj" 477]
   [clojure.main$initialize invokeStatic "main.clj" 508]
   [clojure.main$null_opt invokeStatic "main.clj" 542]
   [clojure.main$null_opt invoke "main.clj" 539]
   [clojure.main$main invokeStatic "main.clj" 664]
   [clojure.main$main doInvoke "main.clj" 616]
   [clojure.lang.RestFn applyTo "RestFn.java" 137]
   [clojure.lang.Var applyTo "Var.java" 705]
   [clojure.main main "main.java" 40]],
  :phase :compile-syntax-check}}

我正在尝试了解回溯,但我对 Clojure/Java 环境不熟悉,而且到目前为止还无法理解。究竟在 Null 值上调用了什么?是因为global-vector 是在main 之外定义的吗? (但是为什么它在 Repl 中起作用呢?)

我在 Java 11.0.2 OpenJDK 64 位服务器 VM 上使用 Clojure 版本:1.10.1.739 和 Leiningen 2.9.4

【问题讨论】:

    标签: clojure leiningen


    【解决方案1】:

    经过进一步调查,我发现它与namespace 分辨率有关。以下表达式:

    (resolve (symbol fn_name))
    

    在 Repl 中工作,但在从“lein run”执行时返回 nil,这反过来会导致 apply 函数引发 NullPointerException

    symbol 函数可以传递一个 ns 参数,所以我通过硬编码我当前的命名空间来修复它,如下所示:

    (resolve (symbol "myproject.core" fn_name))
    

    【讨论】:

      猜你喜欢
      • 2016-03-25
      • 2016-06-06
      • 2017-08-18
      • 2023-04-08
      • 2012-12-31
      • 2016-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多