nicferrier / elnode

evented io webserver right inside your emacs.
http://nicferrier.github.com/elnode
GNU General Public License v3.0
477 stars 48 forks source link

Elnode crashes with symbol-value(closure) #86

Closed rejeep closed 10 years ago

rejeep commented 10 years ago

Hi,

Elnode crashes in version 24.3.1 and 24.3.50.1.

To reproduce:

  1. Clone Cask: https://github.com/cask/cask.git
  2. Checkout the branch elnode-debug
  3. Run: make start-server
  4. Open up a new terminal
  5. Run make unit

This will result in this (this is from 24.3.1, but the result is the same in 24.3.50.1):

$ make start-server
$ cask exec emacs -Q --load 'servant'/app.el --batch > 'servant'/'tmp'/servant.log
`flet' is an obsolete macro (as of 24.3); use either `cl-flet' or `cl-letf'.
`labels' is an obsolete macro (as of 24.3); use `cl-labels' instead.
`flet' is an obsolete macro (as of 24.3); use either `cl-flet' or `cl-letf'.
Package assoc is obsolete!
elnode-error: elnode--sentinel 'open from 127.0.0.1.' for process  *elnode-webserver-proc* <127.0.0.1:57908> with buffer nil
elnode-error: Elnode status: *elnode-webserver-proc* <127.0.0.1:57908> open from 127.0.0.1
elnode-error: filter: calling handler on *elnode-webserver-proc* <127.0.0.1:57908>
...
  symbol-value(closure)
  (and (symbolp func-item) (symbol-value func-item))
  (functionp (and (symbolp func-item) (symbol-value func-item)))
  (or (functionp func-item) (functionp (and (symbolp func-item) (symbol-value func-item))))
  (if (or (functionp func-item) (functionp (and (symbolp func-item) (symbol-value func-item)))) (progn (process-put httpcon :elnode-http-mapping (if (string-match (car pair) path) (progn (progn (let* (... ... ...) (while ... ... ...) (nreverse --cl-var--)))))) func-item))
  (let* ((pair (elnode--mapper-find-mapping path mapping-table)) (func-item (and pair (let* ((v (cdr pair))) (or (and (atom v) v) (car v)))))) (if (or (functionp func-item) (functionp (and (symbolp func-item) (symbol-value func-item)))) (progn (process-put httpcon :elnode-http-mapping (if (string-match (car pair) path) (progn (progn (let* ... ... ...))))) func-item)))
  elnode--mapper-find(#<process *elnode-webserver-proc* <127.0.0.1:57908>> "127.0.0.1//packages/archive-contents" (("^.*//packages/\\(.*\\)$" closure ((routes ("^.*/archive-contents$" closure ((index-file . "/Users/rejeep/Code/cask/servant/packages/archive-contents") (package-directory . "/Users/rejeep/Code/cask/servant/packages") t) (httpcon) (elnode-http-start httpcon 200 (quote ("Content-type" . "text/plain"))) (if (f-exists\? index-file) (elnode-send-file httpcon index-file) (elnode-http-return httpcon (servant--create-index-string package-directory)))) ("^.*/\\([^/]*\\)$" closure ((package-directory . "/Users/rejeep/Code/cask/servant/packages") t) (httpcon) (let ((docroot package-directory) (httpcon httpcon)) (let (...) (if ... ... ...))))) (package-directory . "/Users/rejeep/Code/cask/servant/packages") t) (httpcon) (elnode-hostpath-dispatcher httpcon routes)) ("^.*//new-packages/\\(.*\\)$" closure ((routes ("^.*/archive-contents$" closure ((index-file . "/Users/rejeep/Code/cask/servant/new-packages/archive-contents") (package-directory . "/Users/rejeep/Code/cask/servant/new-packages") t) (httpcon) (elnode-http-start httpcon 200 (quote ("Content-type" . "text/plain"))) (if (f-exists\? index-file) (elnode-send-file httpcon index-file) (elnode-http-return httpcon (servant--create-index-string package-directory)))) ("^.*/\\([^/]*\\)$" closure ((package-directory . "/Users/rejeep/Code/cask/servant/new-packages") t) (httpcon) (let ((docroot package-directory) (httpcon httpcon)) (let (...) (if ... ... ...))))) (package-directory . "/Users/rejeep/Code/cask/servant/new-packages") t) (httpcon) (elnode-hostpath-dispatcher httpcon routes))))
  (or (and extra-table (elnode--mapper-find httpcon path extra-table)) (elnode--mapper-find httpcon path url-mapping-table))
  (let ((handler-func (or (and extra-table (elnode--mapper-find httpcon path extra-table)) (elnode--mapper-find httpcon path url-mapping-table)))) (if elnode--do-access-logging-on-dispatch (progn (process-put httpcon :elnode-access-log-name log-name))) (cond ((functionp handler-func) (funcall handler-func httpcon)) (t (funcall function-404 httpcon))))
  (progn (let ((handler-func (or (and extra-table (elnode--mapper-find httpcon path extra-table)) (elnode--mapper-find httpcon path url-mapping-table)))) (if elnode--do-access-logging-on-dispatch (progn (process-put httpcon :elnode-access-log-name log-name))) (cond ((functionp handler-func) (funcall handler-func httpcon)) (t (funcall function-404 httpcon)))))
  (let* ((function-404 (car (cdr (or (memq (quote :function-404) --cl-rest--) (quote (nil elnode-send-404)))))) (log-name (car (cdr (or (memq (quote :log-name) --cl-rest--) (quote (nil "elnode")))))) (extra-table (car (cdr (memq (quote :extra-table) --cl-rest--))))) (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond ((memq (car --cl-keys--) (quote (:function-404 :log-name :extra-table :allow-other-keys))) (setq --cl-keys-- (cdr (cdr --cl-keys--)))) ((car (cdr (memq ... --cl-rest--))) (setq --cl-keys-- nil)) (t (error "Keyword argument %s not one of (:function-404 :log-name :extra-table)" (car --cl-keys--)))))) (progn (let ((handler-func (or (and extra-table (elnode--mapper-find httpcon path extra-table)) (elnode--mapper-find httpcon path url-mapping-table)))) (if elnode--do-access-logging-on-dispatch (progn (process-put httpcon :elnode-access-log-name log-name))) (cond ((functionp handler-func) (funcall handler-func httpcon)) (t (funcall function-404 httpcon))))))
  elnode--dispatch-proc(#<process *elnode-webserver-proc* <127.0.0.1:57908>> "127.0.0.1//packages/archive-contents" (("^.*//packages/\\(.*\\)$" closure ((routes ("^.*/archive-contents$" closure ((index-file . "/Users/rejeep/Code/cask/servant/packages/archive-contents") (package-directory . "/Users/rejeep/Code/cask/servant/packages") t) (httpcon) (elnode-http-start httpcon 200 (quote ("Content-type" . "text/plain"))) (if (f-exists\? index-file) (elnode-send-file httpcon index-file) (elnode-http-return httpcon (servant--create-index-string package-directory)))) ("^.*/\\([^/]*\\)$" closure ((package-directory . "/Users/rejeep/Code/cask/servant/packages") t) (httpcon) (let ((docroot package-directory) (httpcon httpcon)) (let (...) (if ... ... ...))))) (package-directory . "/Users/rejeep/Code/cask/servant/packages") t) (httpcon) (elnode-hostpath-dispatcher httpcon routes)) ("^.*//new-packages/\\(.*\\)$" closure ((routes ("^.*/archive-contents$" closure ((index-file . "/Users/rejeep/Code/cask/servant/new-packages/archive-contents") (package-directory . "/Users/rejeep/Code/cask/servant/new-packages") t) (httpcon) (elnode-http-start httpcon 200 (quote ("Content-type" . "text/plain"))) (if (f-exists\? index-file) (elnode-send-file httpcon index-file) (elnode-http-return httpcon (servant--create-index-string package-directory)))) ("^.*/\\([^/]*\\)$" closure ((package-directory . "/Users/rejeep/Code/cask/servant/new-packages") t) (httpcon) (let ((docroot package-directory) (httpcon httpcon)) (let (...) (if ... ... ...))))) (package-directory . "/Users/rejeep/Code/cask/servant/new-packages") t) (httpcon) (elnode-hostpath-dispatcher httpcon routes))) :function-404 elnode-send-404 :log-name "elnode" :extra-table nil)
  (let ((hostpath (elnode--hostpath (elnode-http-header httpcon "Host") (elnode-http-pathinfo httpcon))) (extra-table (elnode--auth-entry->dispatch-table auth-scheme t))) (elnode--dispatch-proc httpcon hostpath hostpath-mapping-table :function-404 function-404 :log-name log-name :extra-table extra-table))
  (progn (let ((hostpath (elnode--hostpath (elnode-http-header httpcon "Host") (elnode-http-pathinfo httpcon))) (extra-table (elnode--auth-entry->dispatch-table auth-scheme t))) (elnode--dispatch-proc httpcon hostpath hostpath-mapping-table :function-404 function-404 :log-name log-name :extra-table extra-table)))
  (let* ((function-404 (car (cdr (or (memq (quote :function-404) --cl-rest--) (quote (nil elnode-send-404)))))) (log-name (car (cdr (or (memq (quote :log-name) --cl-rest--) (quote (nil "elnode")))))) (auth-scheme (car (cdr (memq (quote :auth-scheme) --cl-rest--))))) (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond ((memq (car --cl-keys--) (quote (:function-404 :log-name :auth-scheme :allow-other-keys))) (setq --cl-keys-- (cdr (cdr --cl-keys--)))) ((car (cdr (memq ... --cl-rest--))) (setq --cl-keys-- nil)) (t (error "Keyword argument %s not one of (:function-404 :log-name :auth-scheme)" (car --cl-keys--)))))) (progn (let ((hostpath (elnode--hostpath (elnode-http-header httpcon "Host") (elnode-http-pathinfo httpcon))) (extra-table (elnode--auth-entry->dispatch-table auth-scheme t))) (elnode--dispatch-proc httpcon hostpath hostpath-mapping-table :function-404 function-404 :log-name log-name :extra-table extra-table))))
  elnode-hostpath-dispatcher(#<process *elnode-webserver-proc* <127.0.0.1:57908>> (("^.*//packages/\\(.*\\)$" closure ((routes ("^.*/archive-contents$" closure ((index-file . "/Users/rejeep/Code/cask/servant/packages/archive-contents") (package-directory . "/Users/rejeep/Code/cask/servant/packages") t) (httpcon) (elnode-http-start httpcon 200 (quote ("Content-type" . "text/plain"))) (if (f-exists\? index-file) (elnode-send-file httpcon index-file) (elnode-http-return httpcon (servant--create-index-string package-directory)))) ("^.*/\\([^/]*\\)$" closure ((package-directory . "/Users/rejeep/Code/cask/servant/packages") t) (httpcon) (let ((docroot package-directory) (httpcon httpcon)) (let (...) (if ... ... ...))))) (package-directory . "/Users/rejeep/Code/cask/servant/packages") t) (httpcon) (elnode-hostpath-dispatcher httpcon routes)) ("^.*//new-packages/\\(.*\\)$" closure ((routes ("^.*/archive-contents$" closure ((index-file . "/Users/rejeep/Code/cask/servant/new-packages/archive-contents") (package-directory . "/Users/rejeep/Code/cask/servant/new-packages") t) (httpcon) (elnode-http-start httpcon 200 (quote ("Content-type" . "text/plain"))) (if (f-exists\? index-file) (elnode-send-file httpcon index-file) (elnode-http-return httpcon (servant--create-index-string package-directory)))) ("^.*/\\([^/]*\\)$" closure ((package-directory . "/Users/rejeep/Code/cask/servant/new-packages") t) (httpcon) (let ((docroot package-directory) (httpcon httpcon)) (let (...) (if ... ... ...))))) (package-directory . "/Users/rejeep/Code/cask/servant/new-packages") t) (httpcon) (elnode-hostpath-dispatcher httpcon routes))))
  (closure ((routes ("^.*//packages/\\(.*\\)$" closure ((routes ("^.*/archive-contents$" closure (... ... t) (httpcon) (elnode-http-start httpcon 200 ...) (if ... ... ...)) ("^.*/\\([^/]*\\)$" closure (... t) (httpcon) (let ... ...))) (package-directory . "/Users/rejeep/Code/cask/servant/packages") t) (httpcon) (elnode-hostpath-dispatcher httpcon routes)) ("^.*//new-packages/\\(.*\\)$" closure ((routes ("^.*/archive-contents$" closure (... ... t) (httpcon) (elnode-http-start httpcon 200 ...) (if ... ... ...)) ("^.*/\\([^/]*\\)$" closure (... t) (httpcon) (let ... ...))) (package-directory . "/Users/rejeep/Code/cask/servant/new-packages") t) (httpcon) (elnode-hostpath-dispatcher httpcon routes))) (tmp-directory . "/Users/rejeep/Code/cask/servant/tmp") (directory . "/Users/rejeep/Code/cask/servant") (host . "127.0.0.1") (port . 9191) t) (httpcon) (elnode-hostpath-dispatcher httpcon routes))(#<process *elnode-webserver-proc* <127.0.0.1:57908>>)
  funcall((closure ((routes ("^.*//packages/\\(.*\\)$" closure ((routes ("^.*/archive-contents$" closure (... ... t) (httpcon) (elnode-http-start httpcon 200 ...) (if ... ... ...)) ("^.*/\\([^/]*\\)$" closure (... t) (httpcon) (let ... ...))) (package-directory . "/Users/rejeep/Code/cask/servant/packages") t) (httpcon) (elnode-hostpath-dispatcher httpcon routes)) ("^.*//new-packages/\\(.*\\)$" closure ((routes ("^.*/archive-contents$" closure (... ... t) (httpcon) (elnode-http-start httpcon 200 ...) (if ... ... ...)) ("^.*/\\([^/]*\\)$" closure (... t) (httpcon) (let ... ...))) (package-directory . "/Users/rejeep/Code/cask/servant/new-packages") t) (httpcon) (elnode-hostpath-dispatcher httpcon routes))) (tmp-directory . "/Users/rejeep/Code/cask/servant/tmp") (directory . "/Users/rejeep/Code/cask/servant") (host . "127.0.0.1") (port . 9191) t) (httpcon) (elnode-hostpath-dispatcher httpcon routes)) #<process *elnode-webserver-proc* <127.0.0.1:57908>>)
  elnode--handler-call((closure ((routes ("^.*//packages/\\(.*\\)$" closure ((routes ("^.*/archive-contents$" closure (... ... t) (httpcon) (elnode-http-start httpcon 200 ...) (if ... ... ...)) ("^.*/\\([^/]*\\)$" closure (... t) (httpcon) (let ... ...))) (package-directory . "/Users/rejeep/Code/cask/servant/packages") t) (httpcon) (elnode-hostpath-dispatcher httpcon routes)) ("^.*//new-packages/\\(.*\\)$" closure ((routes ("^.*/archive-contents$" closure (... ... t) (httpcon) (elnode-http-start httpcon 200 ...) (if ... ... ...)) ("^.*/\\([^/]*\\)$" closure (... t) (httpcon) (let ... ...))) (package-directory . "/Users/rejeep/Code/cask/servant/new-packages") t) (httpcon) (elnode-hostpath-dispatcher httpcon routes))) (tmp-directory . "/Users/rejeep/Code/cask/servant/tmp") (directory . "/Users/rejeep/Code/cask/servant") (host . "127.0.0.1") (port . 9191) t) (httpcon) (elnode-hostpath-dispatcher httpcon routes)) #<process *elnode-webserver-proc* <127.0.0.1:57908>>)
  (condition-case signal-value (elnode--handler-call handler process) ((quote elnode-defer) (elnode-error "filter: defer caught on %s" process) (if elnode-defer-on nil (elnode-error "filter: no defer timer for %s" process)) (let ((--cl-var-- (elnode--get-server-prop process :elnode-defer-mode))) (cond ((memql --cl-var-- (quote (:managed ...))) (process-put process :elnode-deferred t) (elnode--deferred-add process (cdr signal-value))) ((memql --cl-var-- (quote (:immediate ...))) (elnode-error "filter: immediate defer on %s" process) (funcall (cdr signal-value) process))))) ((quote t) (elnode-error "elnode--filter: default handling %S" signal-value) (process-send-string process (elnode--format-response 500))))
  (unwind-protect (condition-case signal-value (elnode--handler-call handler process) ((quote elnode-defer) (elnode-error "filter: defer caught on %s" process) (if elnode-defer-on nil (elnode-error "filter: no defer timer for %s" process)) (let ((--cl-var-- (elnode--get-server-prop process :elnode-defer-mode))) (cond ((memql --cl-var-- (quote ...)) (process-put process :elnode-deferred t) (elnode--deferred-add process (cdr signal-value))) ((memql --cl-var-- (quote ...)) (elnode-error "filter: immediate defer on %s" process) (funcall (cdr signal-value) process))))) ((quote t) (elnode-error "elnode--filter: default handling %S" signal-value) (process-send-string process (elnode--format-response 500)))) (if (and (not (process-get process :elnode-deferred)) (not (process-get process :elnode-http-started)) (not (process-get process :elnode-child-process))) (progn (elnode-error "filter: caught an error in the handling") (process-send-string process (elnode--format-response 500)) (delete-process process))))
  (let ((handler (elnode--get-server-prop process :elnode-http-handler))) (unwind-protect (condition-case signal-value (elnode--handler-call handler process) ((quote elnode-defer) (elnode-error "filter: defer caught on %s" process) (if elnode-defer-on nil (elnode-error "filter: no defer timer for %s" process)) (let ((--cl-var-- (elnode--get-server-prop process :elnode-defer-mode))) (cond ((memql --cl-var-- ...) (process-put process :elnode-deferred t) (elnode--deferred-add process ...)) ((memql --cl-var-- ...) (elnode-error "filter: immediate defer on %s" process) (funcall ... process))))) ((quote t) (elnode-error "elnode--filter: default handling %S" signal-value) (process-send-string process (elnode--format-response 500)))) (if (and (not (process-get process :elnode-deferred)) (not (process-get process :elnode-http-started)) (not (process-get process :elnode-child-process))) (progn (elnode-error "filter: caught an error in the handling") (process-send-string process (elnode--format-response 500)) (delete-process process)))))
  (save-excursion (goto-char (process-get process :elnode-header-end)) (let ((handler (elnode--get-server-prop process :elnode-http-handler))) (unwind-protect (condition-case signal-value (elnode--handler-call handler process) ((quote elnode-defer) (elnode-error "filter: defer caught on %s" process) (if elnode-defer-on nil (elnode-error "filter: no defer timer for %s" process)) (let ((--cl-var-- ...)) (cond (... ... ...) (... ... ...)))) ((quote t) (elnode-error "elnode--filter: default handling %S" signal-value) (process-send-string process (elnode--format-response 500)))) (if (and (not (process-get process :elnode-deferred)) (not (process-get process :elnode-http-started)) (not (process-get process :elnode-child-process))) (progn (elnode-error "filter: caught an error in the handling") (process-send-string process (elnode--format-response 500)) (delete-process process))))))
  (cond ((memql parse-status (quote (quote header))) (elnode-error "elnode--filter: partial header data received")) ((memql parse-status (quote (quote content))) (elnode-error "elnode--filter: partial header data received")) ((memql parse-status (quote (quote done))) (save-excursion (goto-char (process-get process :elnode-header-end)) (let ((handler (elnode--get-server-prop process :elnode-http-handler))) (unwind-protect (condition-case signal-value (elnode--handler-call handler process) ((quote elnode-defer) (elnode-error "filter: defer caught on %s" process) (if elnode-defer-on nil ...) (let ... ...)) ((quote t) (elnode-error "elnode--filter: default handling %S" signal-value) (process-send-string process ...))) (if (and (not ...) (not ...) (not ...)) (progn (elnode-error "filter: caught an error in the handling") (process-send-string process ...) (delete-process process))))))))
  (let ((parse-status (catch (quote elnode-parse-http) (elnode--http-parse process)))) (cond ((memql parse-status (quote (quote header))) (elnode-error "elnode--filter: partial header data received")) ((memql parse-status (quote (quote content))) (elnode-error "elnode--filter: partial header data received")) ((memql parse-status (quote (quote done))) (save-excursion (goto-char (process-get process :elnode-header-end)) (let ((handler (elnode--get-server-prop process :elnode-http-handler))) (unwind-protect (condition-case signal-value (elnode--handler-call handler process) (... ... ... ...) (... ... ...)) (if (and ... ... ...) (progn ... ... ...))))))))
  (save-current-buffer (set-buffer buf) (insert data) (let ((parse-status (catch (quote elnode-parse-http) (elnode--http-parse process)))) (cond ((memql parse-status (quote (quote header))) (elnode-error "elnode--filter: partial header data received")) ((memql parse-status (quote (quote content))) (elnode-error "elnode--filter: partial header data received")) ((memql parse-status (quote (quote done))) (save-excursion (goto-char (process-get process :elnode-header-end)) (let ((handler ...)) (unwind-protect (condition-case signal-value ... ... ...) (if ... ...))))))))
  (let ((buf (or (process-buffer process) (let* ((port (cadr ...)) (send-string-func (elnode--make-send-string)) (send-eof-func (elnode--make-send-eof))) (process-put process :send-string-function send-string-func) (process-put process :send-eof-function send-eof-func) (set-process-buffer process (get-buffer-create (format " *elnode-request-%s*" port))) (process-buffer process))))) (save-current-buffer (set-buffer buf) (insert data) (let ((parse-status (catch (quote elnode-parse-http) (elnode--http-parse process)))) (cond ((memql parse-status (quote (quote header))) (elnode-error "elnode--filter: partial header data received")) ((memql parse-status (quote (quote content))) (elnode-error "elnode--filter: partial header data received")) ((memql parse-status (quote (quote done))) (save-excursion (goto-char (process-get process :elnode-header-end)) (let (...) (unwind-protect ... ...))))))))
\n")er-Agent: URL/Emacsigest Security/SSLver-proc* <127.0.0.1:57908>> "GET /packages/archive-contents HTTP/1.1
  sleep-for(10)
  sit-for(10)
  (while t (sit-for 10))
  (progn (f-mkdir tmp-directory) (let ((temp-file (f-join tmp-directory "servant.pid")) (temp-buffer (get-buffer-create (generate-new-buffer-name " *temp file*")))) (unwind-protect (prog1 (save-current-buffer (set-buffer temp-buffer) (insert (format "%s" (emacs-pid)))) (save-current-buffer (set-buffer temp-buffer) (write-region nil nil temp-file nil 0))) (and (buffer-name temp-buffer) (kill-buffer temp-buffer)))) (while t (sit-for 10)))
  (if noninteractive (progn (f-mkdir tmp-directory) (let ((temp-file (f-join tmp-directory "servant.pid")) (temp-buffer (get-buffer-create (generate-new-buffer-name " *temp file*")))) (unwind-protect (prog1 (save-current-buffer (set-buffer temp-buffer) (insert (format "%s" ...))) (save-current-buffer (set-buffer temp-buffer) (write-region nil nil temp-file nil 0))) (and (buffer-name temp-buffer) (kill-buffer temp-buffer)))) (while t (sit-for 10))))
  (let* ((port 9191) (host "127.0.0.1") (directory (f-parent (f-this-file))) (tmp-directory (f-join directory "tmp")) (routes (mapcar (function (lambda (it) (cons (format "^.*//%s/\\(.*\\)$" ...) (servant-make-elnode-handler ...)))) (quote ("packages" "new-packages"))))) (elnode-start (function (lambda (httpcon) (elnode-hostpath-dispatcher httpcon routes))) :port port :host host) (if noninteractive (progn (f-mkdir tmp-directory) (let ((temp-file (f-join tmp-directory "servant.pid")) (temp-buffer (get-buffer-create (generate-new-buffer-name " *temp file*")))) (unwind-protect (prog1 (save-current-buffer (set-buffer temp-buffer) (insert ...)) (save-current-buffer (set-buffer temp-buffer) (write-region nil nil temp-file nil 0))) (and (buffer-name temp-buffer) (kill-buffer temp-buffer)))) (while t (sit-for 10)))))
  eval-buffer(#<buffer  *load*> nil "/Users/rejeep/Code/cask/servant/app.el" nil t)  ; Reading at buffer position 1842
  load-with-code-conversion("/Users/rejeep/Code/cask/servant/app.el" "/Users/rejeep/Code/cask/servant/app.el" nil t)
  load("/Users/rejeep/Code/cask/servant/app.el" nil t)
  command-line-1(("--load" "servant/app.el"))
  command-line()
  normal-top-level()
nicferrier commented 10 years ago

Pretty sure I found this problem just now and fixed it. So next release of elnode will include the fix.

rejeep commented 10 years ago

Ok, will try it out then.

nicferrier commented 10 years ago

let me know if it's still broken.