Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FUNC can not be HIJACKed #1012

Open
IngoHohmann opened this issue Sep 23, 2019 · 4 comments
Open

FUNC can not be HIJACKed #1012

IngoHohmann opened this issue Sep 23, 2019 · 4 comments
Assignees

Comments

@IngoHohmann
Copy link

Example:

>> 	g: func[a][a]
== make action! [[a] [...]]
>> 	hijack 'func 'g
PANIC ACTION! called (explicitly or by rebPanic() API)

and in the webconsole I get the following in the log:

2libr3-dca6265.js:1558 fail() @ ../src/include/datatypes/sys-frame.h 907
@hostilefork
Copy link
Member

hostilefork commented Sep 23, 2019

It ideally wouldn't panic (it's a fairly experimental feature). But what would happen in this case won't be good--as hijacking functions in an incompatible way is fairly dangerous.

You're saying everywhere in the system--the console included--is now going to be using your version of FUNC, which takes one argument and not two. (It probably should have stopped you from doing that, but the test for legal/illegal might be tricky e.g. with variadics). It also doesn't actually make an ACTION! and return it, so the console will crash and burn.

Consider doing some harmless tracing, that doesn't sacrifice the original FUNC's purpose to the point of breaking the console:

old-func: copy :func

tracing-func: func [spec body] [
    print ["** FUNC" mold/limit spec 100]
    old-func :spec :body
]

hijack 'func 'tracing-func

From that, I get this before the next console prompt:

** FUNC [
    {Builds up sandboxed code to submit to C, hooked RETURN will finalize}
    item {ISS...
** FUNC [
    {Hooked RETURN function which finalizes any gathered EMIT lines}
    state {Describe...
** FUNC [f [frame!]]
** FUNC [keep [action!] <with> return]

We've got some level of safety in the console due to modularization, but it only protects you against func: func [...] [...] without a HIJACK. That's because console keeps its own copy of the FUNC function pointer (inside a value cell in its module context). So reassigning it in the user context doesn't cause a problem. But when you change the pointed-to function behavior itself, the consequences will be felt by all references.

It would be possible to force the console to COPY all the functions it uses (which is relatively cheap), but that would undermine HIJACK's desire to really deeply hook an implementation for all clients. It's meant to be a power tool that's used sparingly, likely as a stopgap for a better solution.

@IngoHohmann
Copy link
Author

IngoHohmann commented Sep 23, 2019

This was just a minimal version, my original code is:

srcfunc: enclose 'func func [f nf:][
   meta: make object! compose/deep [source: mold/only [func (f/spec) (f/body)]]
   nf: do f
   set-meta :nf meta
   :nf
]

ofunc: :func

hijack 'func 'srcfunc

src: enclose 'source func [f src:][
   if all [action? get :f/arg meta: meta-of get :f/arg src: select meta 'source ][
      print unspaced [src]
   ] else [
      do f
   ]
]

@hostilefork
Copy link
Member

Interesting idea--glad you're getting adventurous with the features. Looking into it...

@hostilefork hostilefork self-assigned this Sep 23, 2019
@hostilefork
Copy link
Member

hostilefork commented Sep 23, 2019

Simpler example:

>> identity: func [x] [x]

>> plusone: enclose 'identity func [f] [1 + do f]

>> plusone 3
== 4

>> hijack 'identity 'plusone

>> plusone 3
** Internal Error: stack overflow
** Where: do _ _ do _ _ do _ _

The FRAME! which is generated for the call to ENCLOSE is not changed by the HIJACK. It's still a frame for the original function. Hence what you get is morally equivalent to a recursion.

But imagine if you had written the function without enclose:

>> identity: func [x] [x]

>> plusone: func [x] [1 + identity x]

>> plusone 3
== 4

>> hijack 'identity 'plusone

>> plusone 3
** Internal Error: stack overflow
** Where: do _ _ do _ _ do _ _ ...

Same problem. You're implementing PLUSONE in terms of IDENTITY. Then you're hijacking identity as implemented via PLUSONE.

Solve via copying. In your case:

srcfunc: enclose (copy :func) func [f nf:] [
   meta: make object! compose/deep [source: mold/only [func (f/spec) (f/body)]]
   nf: do f
   set-meta :nf meta
   :nf
]

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

No branches or pull requests

2 participants