Skip to content

Conversation

@IdWV
Copy link
Contributor

@IdWV IdWV commented Aug 26, 2025

It can be useful to call a resource as if it was a function. This commit adds a way to do so.

When a non-callable is supplied to uc_vm_insn_call(), and it is an object or has a prototype, then the object/prototype stack is searched for a callable named 'operator()', and if found, that is called instead.

So in the script
var(...); is a functional synonym to var'operator()';

It can be useful to call a resource as if it was a function. This commit adds a way to do so.

When a non-callable is supplied to uc_vm_insn_call(), and it is an object or has a prototype, then the object/prototype stack is searched for a callable named 'operator()', and if found, that is called instead.

So in the script
var(...); is a functional synonym to var['operator()']();

Signed-off-by: Isaac de Wolff <idewolff@vincitech.nl>
@jow-
Copy link
Owner

jow- commented Aug 26, 2025

I like the idea, it goes into the direction of Lua's metamethods.

A couple of remarks:

  1. We already have a tostring "metamethod" which is used to obtain a string representation of a resource/object/array, so the function making a resource callable should follow a similar naming convention. I'd lean towards a simple invoke or similar property name instead of operator() which is more C++-esque
  2. We need to decide if an invoke metamethod may be another object with an invoke metamethod (loops!) or whether it must be a true function or cfunction
  3. The ucv_is_callable() API function needs to report such values-with-invoke-metamethod as callable too
  4. You can use ucv_property_get(val, "propname") to find a property in the object itself or its prototype chain
  5. The arbitrary search depth limitation of 16 should be removed, it could lead to unexpected behavior with very deep prototype chains
  6. In case of dictionaries, I am not sure if we should look for the invoke "metamethod" in the dictionary itself. I think it is better to only search it in the prototype chain, this way user objects will not accidentally trigger this behavior as prototypes are rare and explicitly set while plain dictionaries may be constructed from any number of sources.

@IdWV
Copy link
Contributor Author

IdWV commented Aug 26, 2025

  1. To avoid unwanted collisions, I first thought about using some GUID or so. But I decided 'operator()' is rare enough to be not used by accident.
    On the other hand, if an object has an executable member 'invoke', than it's designed to be invoked, and the function doesn't (need to) know it's name is omitted in the script. The behaviour is the same. So that won't hurt.
  2. Not only loops, also branches. An object could have an non-executable invoke member, which has an executable invoke member in its prototype chain. Meanwhile the object can have an executable invoke in it's own chain either.
    Further the 'this' needs to make sense to the invoke function. The more complex the construction, the more ways to screw it up. So I tend toward keeping the chain a chain, and not making it a tree.
  3. When using ucv_prototype_get() a non-executable invoke can hide an executable one. Is that wanted behaviour?
  4. Shouldn't the prototype chain be guarded against loops, in that case?
  5. 'accidentally trigger this behavior'. The behaviour is in this case not throwing an "left-hand side is not a function", but executing a member function instead. Unless someone wrote a script which relies on that exception to be thrown, it won't break existing scripts. But i can imagine that for new scripts this can cause some head-scratching. I'm fine with restricting this to the prototype chain.

@IdWV
Copy link
Contributor Author

IdWV commented Aug 26, 2025

The ucv_is_callable() API function needs to report such values-with-invoke-metamethod as callable too

Is is OK to change the inline function to an exported function?

@jow-
Copy link
Owner

jow- commented Aug 26, 2025

Yes, should be fine

jow- and others added 7 commits August 27, 2025 19:47
Implement two new formats `X` and `Z` which encode/decode C memory to/from
hexadecimal and base64 strings, respectively.

This is useful to extract binary data payloads from a given structure buffer
directly into ucode string encoded in a printable format suitable for use in
JSON objects and similar formats requiring printable character encoding.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This uses socketpair() to return a pair of connected sockets.
The function returns an array with a socket instance as the first element
and a file descriptor number for the other side.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
This opens an existing file descriptor as a socket instance.
Can be used to use open sockets received through ubus.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
- Verify the instruction number before using it as index into the
  `insn_names` and `insn_operand_bytes` arrays to avoid reading past
  the end of those arrays.

- Decode `UVAL` sub-instruction as u8, not u32

Fixes: jow-#321
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Handle TXT records with multiple string elements by space-joining the
individual substrings. Also introduce a new, default dsabled boolean
option `txt_as_array` which makes the resolv module to return TXT record
values as ucode arrays containing the individual strings as elements.

Fixes: jow-#315
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
The current implementation of rand() returns a number in an unknown (to
the ucode script) range. This commit adds an optional range to rand().

For example the call

    let r = math.rand( 7, -3.1 );

returns a number in the range 7 ... -3.1, inclusive.

Signed-off-by: Isaac de Wolff <idewolff@vincitech.nl>
[align code style, minor whitespace spaces & rewrapping]
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Signed-off-by: Isaac de Wolff <idewolff@vincitech.nl>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants