As far as I know there isn't, but just in case I missed something, is there a way in Sci to control what methods can be invoked on objects and what not? I'm looking for something more granular than {:classes {'java.io.Reader java.io.Reader}}, maybe something like {:classes {'java.io.Reader {'close http://java.io.Reader/close}}}` . Ideally, I would be able to provide an implementation per method like what we can do with functions.
With functions I can build a decent sandbox (e.g. no IO to certain resources), but the moment I allow certain classes, let say java.io.Reader I give access to my whole filesystem
is this for a binary or JVM usage?
JVM usage
ok, we can accomplish this for sure but it needs some work. with static methods it's already possible
in binaries you can control the access via the reflection config
Ah cool. Is it something I could work on or is it quite involved?
for the JVM I mean
the main thing would be to prevent performance loss for the paths that don't use this.
you can see how I implemented it for static methods in babashka impl/classes.clj + SCI
Ok thanks I will have a look
It seems at the moment you can override static methods, but you can't block them individually. E.g. the following adds a new static method, but this doesn't block the other ones:
(sci/eval-string "(Class/forName \"java.lang.String\")"
{:imports {'Class 'java.lang.Class}
:classes {'java.lang.Class {:class Class :static-methods {'SomethingElse (fn [& _] :dude)}}}}) ;=> java.lang.String
I will have a look at the implementation nowtrue
but probably related since in SCI we search if there is an override
and at that spot you could also say: we won't execute this
Yeah probably it could be mixed with something here https://github.com/babashka/sci/blob/b0d06162a4a03abee2045075833508b0f5b6b095/src/sci/impl/analyzer.cljc#L1043-L1050 I can imagine that doesn't have to add any additional performance loss
Maybe naive observation, but the function is called analyze-dot , but there is no . in static calls. I guess I shouldn't take this too literal and it refers just to interop in general
(. Thread sleep 1000)Ah of course 😅
I'm guessing to add control over instance methods we would have to add a conditional branch as with the :static-methods in the analyzer https://github.com/babashka/sci/blob/407aa71dd0e6e20de68494b4b0d365d7df2a3fd1/src/sci/impl/analyzer.cljc#L998-L1002 . So if we do it like that, for all paths that don't use this there would be an overhead of a failing test:
(if-let [f (some-> ctx :env deref :class->opts :instance-methods (get (interop/fully-qualify-class ctx instance-expr))
(get method-expr))
)
Is that potentially acceptable or likely to cause too much loss in performance?Maybe this is even simplified because there could be several matching classes actually
A tiny optimization could be changing the whole some-> to (-> ctx :env deref (some-> :class->opts :instance-methods ...)) as the first bit will always be there I think so no need to add the overhead of tests
is that change inside of the node?
Not sure what you mean by that. I was thinking that we would have to add a similar test as with the :static-methods. So something like this if-let block https://github.com/babashka/sci/blob/407aa71dd0e6e20de68494b4b0d365d7df2a3fd1/src/sci/impl/analyzer.cljc#L1032-L1040 For the instance-methods we maybe also have to walk a class hierarchy, so maybe a more involved test. Maybe this could be cached as with protocols
what I mean is: would your check be inside sci.impl.types/->Node or outside. outside is better but we probably don't know the target class
I don't know yet. I thought it could be outside, but maybe it is time for some experimentation and see how far I can get
I've created a first implementation in this https://github.com/babashka/sci/pull/1027 . It is passing basic tests, but I will test it on my project as well to see if it is sufficient in practise.