   "Implementation of ‘elisp/runfiles/env-vars’ for manifest-based runfiles.
 RUNFILES is a runfiles object, and REMOTE is the remote host
 identifier."
-  (with-slots (filename) runfiles
+  (let ((filename (elisp/runfiles/runfiles--manifest-filename runfiles)))
     (unless (equal (file-remote-p filename) remote)
       (signal 'elisp/runfiles/remote (list filename remote)))
     (list (concat "RUNFILES_MANIFEST_FILE="
                   (file-name-unquote (file-local-name filename)))
           "RUNFILES_DIR" "JAVA_RUNFILES")))
 
-(defclass elisp/runfiles/runfiles--directory (elisp/runfiles/runfiles)
-  ((directory :initarg :directory :type string))
-  :documentation "Directory-based runfiles implementation.")
+(cl-defstruct (elisp/runfiles/runfiles--directory
+               (:constructor elisp/runfiles/directory--make (directory))
+               (:copier nil))
+  "Directory-based runfiles implementation."
+  (directory nil :read-only t :type string))
 
 (defun elisp/runfiles/make--directory (directory)
   "Create a directory-based runfiles object for DIRECTORY.
 Return an object of class ‘elisp/runfiles/runfiles--directory’."
-  (elisp/runfiles/runfiles--directory
-   :directory (file-name-as-directory (expand-file-name directory))))
+  (elisp/runfiles/runfiles--make
+   (elisp/runfiles/directory--make
+    (file-name-as-directory (expand-file-name directory)))))
 
 (cl-defmethod elisp/runfiles/rlocation--internal
   ((runfiles elisp/runfiles/runfiles--directory) filename)
   "Implementation of ‘elisp/runfiles/rlocation’ for directory-based runfiles.
 RUNFILES is a runfiles object and FILENAME the name to look up."
-  (expand-file-name filename (oref runfiles directory)))
+  (expand-file-name filename
+                    (elisp/runfiles/runfiles--directory-directory runfiles)))
 
 (cl-defmethod elisp/runfiles/env-vars--internal
   ((runfiles elisp/runfiles/runfiles--directory) remote)
   "Implementation of ‘elisp/runfiles/env-vars’ for directory-based runfiles.
 RUNFILES is a runfiles object, and REMOTE is the remote host
 identifier."
-  (with-slots (directory) runfiles
+  (let ((directory (elisp/runfiles/runfiles--directory-directory runfiles)))
     (unless (equal (file-remote-p directory) remote)
       (signal 'elisp/runfiles/remote (list directory remote)))
     (let ((directory (file-name-unquote (file-local-name directory))))

             (_ (setq continue nil)))))
       (or result
           (signal 'elisp/runfiles/not-found
                   (list filename (oref runfiles filename)))))
      ((eq result :empty)
       (signal 'elisp/runfiles/empty
               (list filename (oref runfiles filename))))
      (t result))))

(cl-defmethod elisp/runfiles/env-vars--internal

           (puthash key :empty manifest))
          (other (signal 'elisp/runfiles/syntax-error (list filename other))))
        (forward-line)))
    (elisp/runfiles/runfiles--make
     (elisp/runfiles/manifest--make filename manifest))))

(cl-defmethod elisp/runfiles/rlocation--internal
  ((runfiles elisp/runfiles/runfiles--manifest) filename)
  "Implementation of ‘elisp/runfiles/rlocation’ for manifest-based runfiles.
RUNFILES is a runfiles object and FILENAME the name to look up."
  (let ((result
         (gethash filename
                  (elisp/runfiles/runfiles--manifest-manifest runfiles))))
    (cond
      ((not result)
       ;; Look for ancestor directory mapping.  See
