1

I'm dealing with a third-party package that uses Structures - cl-defstructs.

One of the slots is :read-only t. I need to override that. I need to find a way to change the value in that slot and then pass the structure to a function, essentially "deceiving" that function.

Let's say I have:

(cl-defstruct (toy (:constructor toy-create)
                   (:copier nil))
    (color nil :read-only t))

Somewhere within that third-party package, it creates an instance of toy, e.g.:

(setq stupid-toy (toy-create :color 'red))

Now, let's say I need to change :color to 'green and pass this "new object" into some function.

I'd try overriding slot-accessor function - toy-color, right before calling that function:

(cl-labels ((toy-color (x) 'green)) ;; now, whatever within (break-it) asks for toy's color, it should be 'green, right?
   (funcall 'break-it stupid-toy)))
   

It seems that doesn't work. My fake toy-color fn doesn't get called. Maybe because :color is read-only?

What are my options here? I cannot use copy-toy because copier-fn is disabled (see above) Can I create a new struct type that inherits from toy through :include option, but :color no longer read-only? How do I create/clone a new struct from the old one? Remember, in my example, the struct has only one slot; the real struct type that I'm dealing with has over dozen of slots.

iLemming
  • 1,223
  • 9
  • 14

1 Answers1

0

I think I figured it out. I dug into code and tried learning how oset works. Usually, to set a slot value you'd call it like this: (oset stupid-toy color 'green), but in my case it wouldn't work, since the slot is read-only, it would complain.

Turns out, oset internally calls eieio-oset, reading its source, revealed that it's possible to set the value bypassing validation:

(let*  ((class (eieio--object-class stupid-toy)
        (idx (or (eieio--slot-name-index class 'color)
                (eieio--class-slot-name-index class 'color')))))
    (aset stupid-toy idx 'green))

You need to figure out the index of the slot, and then you can set the value with aset. A bit tricky, yes, but can be done.

Update: Turns out there's much better and more straightforward way:

(setf (cl-struct-slot-value 'toy 'color stupid-toy) 'green)
iLemming
  • 1,223
  • 9
  • 14