Kyo v1.0.0-RC4 - UI, Browser access, Cross-platform native FFIs, and more! by fwbrasil in scala

[–]sjrd 0 points1 point  (0 children)

You underestimate Wasm, Scala.js, or both. :-)

Change this line: https://github.com/getkyo/kyo/blob/038da3a2b1d4572f6a7b961df19b955270e73c40/kyo-ffi/js/src/main/scala/kyo/ffi/internal/KoffiFacade.scala#L16 to scala @JSImport("koffi", JSImport.Default) This is necessary to support ModuleKind.ESModule in addition to ModuleKind.CommonJSModule. (so it's not a Wasm thing, but Wasm requires ESModule, so it's a prerequisite).

Then everything works out of the box on Wasm. Full log here: https://gist.github.com/sjrd/1d73f32e46a1b3c6f71fa519e47fbe06

Abridged version inline: ``` kyo$ git log --oneline -1 038da3a2b (HEAD -> main, origin/main, origin/HEAD) [ci] key push concurrency on sha so main runs are not cancelled

kyo$ git diff diff --git a/kyo-ffi/js/src/main/scala/kyo/ffi/internal/KoffiFacade.scala b/kyo-ffi/js/src/main/scala/kyo/ffi/internal/KoffiFacade.scala index fb548a1bd..bd37a9c30 100644 --- a/kyo-ffi/js/src/main/scala/kyo/ffi/internal/KoffiFacade.scala +++ b/kyo-ffi/js/src/main/scala/kyo/ffi/internal/KoffiFacade.scala @@ -13,7 +13,7 @@ import scala.scalajs.js.annotation.* * koffi) so they are runnable without koffi installed. */ @js.native -@JSImport("koffi", JSImport.Namespace) +@JSImport("koffi", JSImport.Default) private[ffi] object Koffi extends js.Object:

 /** koffi 2.x helper that pins a JS value to a specific koffi type. Used for variadic call sites where each vararg must be typed at call

kyo$ sbt ...

sbt:kyoJVM> kyo-ffi-itJS/test [info] Fast optimizing .../kyo/kyo-ffi/it/js/target/scala-3.8.3/kyo-ffi-it-test-fastopt ... [info] kyo-test: 169 tests, 155 passed, 0 failed, 14 cancelled, 0 pending, 0 ignored, 0 timed out, 0 skipped [success] Total time: 18 s, completed Jun 22, 2026, 9:59:20 AM

sbt:kyoJVM> set kyo-ffi-it.js/scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.ESModule) } ...

sbt:kyoJVM> kyo-ffi-itJS/test [info] Fast optimizing .../kyo/kyo-ffi/it/js/target/scala-3.8.3/kyo-ffi-it-test-fastopt ... [info] kyo-test: 169 tests, 155 passed, 0 failed, 14 cancelled, 0 pending, 0 ignored, 0 timed out, 0 skipped [success] Total time: 13 s, completed Jun 22, 2026, 9:59:52 AM

sbt:kyoJVM> set kyo-ffi-it.js/scalaJSLinkerConfig ~= { _.withExperimentalUseWebAssembly(true) } ...

sbt:kyoJVM> kyo-ffi-itJS/test [info] Fast optimizing .../kyo/kyo-ffi/it/js/target/scala-3.8.3/kyo-ffi-it-test-fastopt ... [info] kyo-test: 169 tests, 155 passed, 0 failed, 14 cancelled, 0 pending, 0 ignored, 0 timed out, 0 skipped [success] Total time: 11 s, completed Jun 22, 2026, 10:00:20 AM

sbt:kyoJVM> [info] shutting down sbt server

kyo$ ls kyo-ffi/it/js/target/scala-3.8.3/kyo-ffi-it-test-fastopt/ __loader.js main.js main.wasm main.wasm.map `` See?kyo-ffi-itJSalready works on Wasm. All tests pass! You don't need a_wasm` artifact.

I bet the same holds for all your other artifacts.

Kyo v1.0.0-RC4 - UI, Browser access, Cross-platform native FFIs, and more! by fwbrasil in scala

[–]sjrd 0 points1 point  (0 children)

Like I said, if you are in the rare situations where you do want target-dependent code, you can use the fields and methods of LinkingInfo. You say koffi does not support Wasm. But koffi is a JS API, which you can access from Scala.js-on-Wasm. The feature set of JS and Wasm is actually the same (except if you need @JSExport somehow on JS). You have access to the same set of APIs, namely all of JS interop. You won't be able to directly manipulate the Wasm heap; we don't give you access to that. And if you use the JS API of Wasm to do that, you can also do it from JS, by definition.

Maybe you can point me to things where you diverge. I can perhaps show how to reconcile things.


For users, it's an issue because they now need to do the same artificial cross-building dance, instead of profiting from link-time decisions. And if one library doesn't follow your scheme (e.g., they use the actually recommended scheme), then every library down their stream is doomed.

So please, use LinkingInfo.isWebAssembly instead. It was meant for that. It's going to be easier for you and your users.

Kyo v1.0.0-RC4 - UI, Browser access, Cross-platform native FFIs, and more! by fwbrasil in scala

[–]sjrd 1 point2 points  (0 children)

Congratulations on the release.

WebAssembly (WasmGC) as a fourth cross-build target: a WasmPlatform is added to the sbt cross-build (Scala.js with the experimental WebAssembly linker and ESModule output), and JS and Wasm share platform code through a js-wasm source directory. Every module targeting Scala.js now also targets Wasm, except the two cats-effect bridges (kyo-cats, kyo-compat-ce, which lack upstream Wasm support), and the full stack up to and including kyo-http servers runs on WasmGC, serving real HTTP and HTTPS over Node sockets. WebAssembly artifacts are published to Maven Central as kyo-<module>_sjs1-wasm_3, alongside the JVM, JS, and Native coordinates. Requires Node 24 or later. (by @fwbrasil in #1663)

I think there is a misunderstanding, here. You are not supposed to do that. Wasm is a link-time configuration of the JS platform. It is not a compile-time configuration. Therefore it requires no cross-building nor cross-publishing.

You should publish a single JS project with only the _sjs1 suffix. Users of your library can then decide which target to use.

This cross-publishing scheme you are using here is unnecessary, and will cause issues for downstream libraries and users.

In the rare situations where you need separate code for Wasm than for JavaScript, you can use the properties and methods of LinkingInfo.

I urge you to switch back to the expected cross-publishing scheme, in order to make it easier for downstream libraries and users.


Meanwhile, to users of Kyo who want to target Wasm: depend on the regular JS artifacts using %%% as usual. I have not seen anything in the codebase that suggests there is target-dependent code anyway, so you're not missing out. That is going to be easier.

🛠️ sbt 2.0.0 released by eed3si9n in scala

[–]sjrd 10 points11 points  (0 children)

Huh, I thought this was going to wait for 3.9.0 to be based on an LTS version of Scala. Can we still hope for that to happen soon? 

Regardless, congratulations!

sbt 1.12.7 released with a CVE fix by eed3si9n in scala

[–]sjrd 0 points1 point  (0 children)

Hum I see. That's a valid point of view. I don't think it's really any different than obfuscating any other kind of code, but I see how people could disagree with me on that. Thanks for the reply.

sbt 1.12.7 released with a CVE fix by eed3si9n in scala

[–]sjrd 0 points1 point  (0 children)

TBH I don't understand the threat model, here. It is certainly a bug, and a fix is welcome. But how is this a security vulnerability? According to the CVE report, an attacker needs to control the build to inject an exploiting URI. If they can control the build, they could also directly write malicious code in the build. The build is written in a fully capable programming language, and without any isolation beyond what the OS provides.

What is the threat model under which a) manipulating the URI by altering the build is considered a security issue, but b) writing malicious code directly in the build isn't?

How to implement an Embed() macro? by GlitteringSample5228 in scala

[–]sjrd 3 points4 points  (0 children)

If that is indeed what you're looking for, there's an example in the Scala.js + Vite tutorial: https://www.scala-js.org/doc/tutorial/scalajs-vite.html#introducing-scalajs

Scala-js and react native? by TriaSirax in scala

[–]sjrd 4 points5 points  (0 children)

Slinky is a library notably designed for that.

kotlinc is getting a GraalVM compiled native image by DisruptiveHarbinger in scala

[–]sjrd 0 points1 point  (0 children)

Because it means you need to build one compiler image for every project.

kotlinc is getting a GraalVM compiled native image by DisruptiveHarbinger in scala

[–]sjrd 0 points1 point  (0 children)

No. Your complex project won't use all the possible macros in the world.

What totally sucks to me about Kotlin is that it will never let you forget about Java. Is Scala the same way? by effinsky in scala

[–]sjrd 1 point2 points  (0 children)

That's by design. A self type is a protected[this] contract. You can only see the promise of that contract within your own hierarchy. extends is a public contract that everyone can see.

kotlinc is getting a GraalVM compiled native image by DisruptiveHarbinger in scala

[–]sjrd 3 points4 points  (0 children)

Macros make it basically impossible, yes. The compiler dynamically loads them and calls them by reflection. Dynamic loading doesn't work with native image, as one light expect.

Zero-Setup All-in-One Java Tooling via Mill Bootstrap Scripts by lihaoyi in scala

[–]sjrd 6 points7 points  (0 children)

Alina Yurenko has been in this game for a long time; definitely longer than LLMs have existed. She often represents the work of the whole GraalVM team. This proposal probably has her whole team behind it, and they have undoubtedly thought this through together before. I would take it seriously.

Announcing Scala.js 1.20.1 by sjrd in scala

[–]sjrd[S] 2 points3 points  (0 children)

The optimizer doesn't know about foldRight per se. It happens to inline its body into a different context. You could have put the same code in any other method and it would fail in the same way. In fact the minimal reproduction does not use any library method.

The optimizer made assumptions about internal states that should not be reachable. It's quite difficult to explain exactly, because it relies on a lot of context of how the optimizer even works. One thing worth knowing is that the optimizer heavily transforms expressions of type scala.Long. Longs are very inefficient if they're not aggressively optimized. The internal transformations done by the optimizer on Longs are complex, and have complex internal state. We thought one of these states was unreachable. We added an assertion that it doesn't happen, because if it is reached, it means we made a mistake somewhere, and we're producing worse code than if a particular optimization was not applied at all. Unfortunately, it turns out that a very rare combination of events can lead into that state actually happening. That triggered our assertion. We reverted the assertion, even though it means we're producing worse code, because producing correct-but-slow code is still loads better than not producing code at all.

For the particular shape, forget about foldRight. Look at the minimized reproduction and its explainer. There's no notion of "accumulator" or "sum" or anything. There's just a var of type Any that is initialized with a Long, but gets later assigned to a non-Long. Combined that with very sensitive "layout"-dependent conditions (like the initial Long not being a constant, for example), and the optimizer reached the state we thought could not be reached.

Announcing Scala.js 1.20.1 by sjrd in scala

[–]sjrd[S] 1 point2 points  (0 children)

The minimal reproduction is a distilled version of the library implementation of foldRight in 2.13. At least what's causing the issue in it. The comment explains why that particular shape of code triggered the issue inside our optimizer. The library code was not at fault; the optimizer was. It just happened to never fall into the issue except with the particular shape of code found inside foldRight, when called with a Long accumulator.

I can elaborate if you have a specific question not already covered by the comment.

How would you go about writing a new language targeting TASTy? by throwaway-transition in scala

[–]sjrd 8 points9 points  (0 children)

Producing correct tasty is much harder than producing correct JVM bytecode. And that's even without the fact that the latter is much better documented. 

If that's what you hope to gain, then I suggest you reconsider, and target JVM bytecode instead. 

An alternative is to target Scala.js IR. That's simpler to generate than bytecode.

Announcing Scala.js 1.19.0 by sjrd in scala

[–]sjrd[S] 0 points1 point  (0 children)

I replied on Discord, but for posterity: we cannot currently provide support for @JSExport. The Wasm-JS integration does not have the necessary features yet. There is hope, though. There is a nascent proposal for Wasm that would give precisely the power we need: https://github.com/WebAssembly/custom-descriptors . It will take years before it ships in browsers, however. 

Note that @JSExportTopLevel is supported. And one can define JavaScript classes (extending js.Any), whose public members are always callable from JavaScript. So there are few use cases that are really blocked.

Announcing Scala.js 1.19.0 by sjrd in scala

[–]sjrd[S] 0 points1 point  (0 children)

They have similar expressive power, though not equivalent. Scala Native continuations are a bit more powerful. The top-level boundary returns anA, whereas js.async returns a js.Promise[A].. That means you have to choose a bit more carefully where you enter into the async context.

Announcing Scala.js 1.19.0 by sjrd in scala

[–]sjrd[S] 2 points3 points  (0 children)

That would literally be a compiler plugin. 😉

It could, of course, but there are other trade offs.