sci 2026-07-07

Is it correct that it is not possible to override constructors in JVM Sci?

It looks like it is possible with Dart now. I would like to use it on the JVM if possible

that's probably a gap in the JVM impl. I'd be fine receiving an issue or PR for it

👍 1

probably similar to :static-methods

since we know the classname at analysis time

what are you making if I can ask? (this always motivates me to continue working on things ;))

Trying to build a sandbox, originally for a web framework, but not sure anymore

good answer. trying to build something I'm not sure what it's going to become anymore.

haha yeah not a great motivator maybe

experimental mode = the fun mode ;)

🚀 1

But for a proper sandbox the constructors need to be tackled anyway. I was now thinking about a filesystem abstraction where java.io.File needs to work, but not with all the priviliges from the JVM

yeah, a read-only java.io.File for example?

yeah and/or only subdirectory access. So no access to the parent directory

ah for an AI thing?

it could be used for that

People are using vms which are cheap these days, but maybe a Sci sandbox is cheaper

@jeroenvandijk the native tests serialize everything by shelling out

so passing functions doesn't work

just skip it on native

ah i was just typing i have to fix graal. Thanks

I guess we also want to deny constructors like how you can deny :instance-methods? So when there is :closed true • and no :constructor then no constructor access • :constructor true => use normal constructor Otherwise • :constructor fn => call fn as contructor • :constructor true => use normal constructor

yes, I was also thinking about that. correct

I'm making the changes in the right place right, in analyze-new

yep, similar as others

or is this a question?

yes sorry, for confirmation

I think (Foo. ...) goes through analyze-new but it'd be good to check

✔️ 1

if so yes

👍 1

Ok did an update. I didn't work on any deny or override logic on the cljs or cljd part

fine. let's fix clj first

I added a question on gh

> why is there only one top level :closed for constructors? Ah, uh because there is only one constructor? And :constructor is never a map. But yeah what if you just want to deny the constructor 🤔

can you give an example of what you have now and what would be the alternative? not sure what you mean with top level

No you are right, there is something missing. But i don't know yet what would be best.

{:class String
 :close true ;; <= top level
 :constructor true ;; or (fn [_])
 :instance-methods { :close true ;; <= section level
                     'length true }}
But what if we want just deny a :constructor and allow everything else. That's not implemented. So maybe via
{:class String
 :constructor false}
But that is a deviation from the rest

you just add a function that throws I'd say

it's :closed btw

ah yeah true. In the tests i have :closed 😅

yeah probably the easiest indeed to do it via a function. I can add a test just to illustrate this. The behaviour is already there

oops, sorry I forgot about your PR in the latest SCI release... too caught up with the JS JIT stuff ;)

Yeah no worries. I like your JS JIT stuff

I think this approach could work on the JVM as well

I haven't looked into how it actually works, but is it something like estimating if the analyzed code can safely be evaluated with the system eval ?

haha it depends 🙂

it precompiles certain shapes e.g. for loops

but it's all stll integrated with the interpreter. some parts can be interpreted while numerical operations and loops can still be compiled

Can it work together with :interrupt-fn?

yes it does

cool, i have to learn how this works

Or maybe not 😅 It's pretty advanced low-level js code

Great you could make it work

I'm guessing with a clj version the thing that would be send to clojure's eval would be more clojury

It's all isolated to one little namespace (600 lines). The structure of the interpreter we already set up for this

In Clojure we could do a similar thing: produce clojure code that goes through eval, but calls back into the interpreter when it needs "escapes"

but the problem on the JVM is that many eval s can get you into some kind of problem since every eval produces a new classloader I think

Ah yeah I was just thinking that. So then you are thinking of combining it with Crema? I saw an adr about it in the commits

Crema could also work, but you would still need clojure's eval - or you could indeed do a direct byte code thing

crema is only interesting for native-images

here I was thinking about JVM usage

👍 1

> since every eval produces a new classloader So this would produce some overhead? That would reduce the performance gains?

I don't know. and do many people use SCI on the JVM for sandboxing? is it worth the effort? also don't know :)

for JS it was certainly worth it

(and it only added 5kb gzip to the bundle)

Yeah good questions. Maybe once the idea is out people will start to report the desire

Longer term I would be interested in a performance tweak like this, but for now I'm fine i think

It mostly matters for hot loops

In a previous project I had my own very basic clojure interpreter where I was using eval in production. It was to create custom pricing functions. I didn't discover any problems at the time (it was high volume traffic)

But it was always something like (eval '(fn [~args] ... )) and then the function would be called during runtime

Ah I see. Too bad he doesn't share some example code that is being eval-ed

each fn is compiled to a class

Ah: The issue was solved by adding JVM option -XX:MaxMetaspaceSize=512m. Turns out, code generated by (eval) doesn't have a GC root. So when we limit the metaspace, its GC is triggered as expected

👍 1

In our production setup, I think we would have maximum 200 evalled functions at a time. With some updates, maybe 2000 a day. But then our instances were rolled over via autoscaling. So instances would not live much longer than a few days. Maybe we were lucky 😁

so maybe not an issue at all

Cool. Also the https://stackoverflow.com/questions/71447267/clojure-memory-leaks-using-eval is probably not how you would want to do it. It is calling eval a million times. Probably you would do it max only a few hundred times in code base at analyse time (depending on how big the code base is)? And then call the compiled function at runtime?

true, it's only a limited set of evals per analyzed node. but I guess people could call sci/eval-string millions of times

anyway, they could set the perm gen space and maybe jit should be opt-in

I now made it opt-out so people using SCI on JS will actually see the benefits sooner

Yeah true. Maybe sci/eval-string can have an internal counter for mal practise 🙈. Better practise would be to do (sci/eval-string "(fn [...] ..)})") , no?

opt-out is also a good strategy. I think it is hard to make everything fool proof

to re-use that fun? yeah, I don't know - it depends what you want I guess

That's how I do it mostly. Most things are static, most of the time. Doesn't need re-evaluation in a hot loop. In my cases so far I think

it uses SCI in JVM (non graal)?

So pretty hot loops I'm guessing

I mean, hot loops in SCI code. database functions usually don't contain hot loops

but re-use most of what is mapped in by the config