develooper Front page | perl.perl5.porters | Postings from August 2023

Using Magic to check and veto modifications to SVs

Thread Next
Paul "LeoNerd" Evans
August 18, 2023 19:41
Using Magic to check and veto modifications to SVs
Message ID:
I feel the need to add a new function to the magic structure, in order
to implement a whole category of feature that currently is not
possible. But the more I stare at it, the more I don't have a good feel
for how it might work. So maybe folks here can help?

Currently, regular scalar magic has two main functions of interest - the
`get` function, and the `set` function. The `get` function is invoked
early during a read access on a scalar, and can put whatever result it
likes into the SV, making reads appear to have whatever value it
dreamed up. The `set` function is invoked late during a write access on
the scalar, and is simply informed "by the way this is your new value,
deal with it".

The problem for me with `set` magic is that it applies very late. The
value has already been assigned into the SV, we're just being told it
happened. So a simple application of this magic isn't good enough to
veto modification of the SV. It's too late, it's already been done.

I've been attempting to use magic on scalar variables in order to
implement constraint checking - two examples being in these two modules

The approach I take in both modules above is to store a shadow copy of
the value each time a successful `set` operation happens. Then if I
want to veto a value after a `set`, I can copy that previous known-good
value back into it (taking care not to invoke `set` magic a second
time). From a user perspective it works fine, though it's a tiny bit
slow and has unfortunate consequences in cornercases like using
is_refcount() tests or getting Devel::MAT to track down object
references. Those extra references are seen as extra copies of data
floating about the place, upsetting refcount expectations, and so on.

An alternative approach would be to implement these much like a tied
scalar would; wherein the tied SV itself is only the "interface" onto
some different storage elsewhere, and at `set` time we copy accepted
values, remembering to clear out the interface SV in the meantime. A
downside of that approach is that now you have to implement `get` magic
too, because the magical SV itself isn't the real storage of the data.
This would slow down read accesses, and I feel would be an annoying
cost to pay for what I intended to be a commonly-used attribute. Most
object fields get read from far more often than they get written to, so
it would be nice for reads to remain as fast as possible.

I'd therefore started to think about adding a new function to the magic
vtable that can look at the proposed new value for assignments before
the modification is actually made, allowing it to veto the new value if
it didn't like it.

Annoyingly, I don't currently have a good feel for what the interface
should look like. As the magic (target) SV itself hasn't yet been
modified, it would need to take the source value too, so perhaps its
signature would be something of the form

  int (*svt_check) (pTHX_ SV *sv, MAGIC *mg, const SV *srcsv);

Invoking this from sv_setsv() and friends would then be quite easy. An
as-yet unanswered question is how on earth do any other SV-modifying
functions interact with this. When calling something like sv_setiv()
there isn't a "source SV" to pass in. As `check` magic might be
relatively rare, maybe we'd create a temporary SV containing the
proposed new value to pass in instead? But that's starting to sound
like it would still get quite expensive, and maybe the whole idea is

Does anyone else have any ideas? Some way to go about checking
potential values to be assigned into SVs and vetoing ones we don't like
the look of, ideally without interrupting the read path at all..?

Paul "LeoNerd" Evans      |  |

Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About