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

GraalVM Native Image Requires Config for lazy vals #13985

Closed
jamesward opened this issue Nov 21, 2021 · 28 comments · Fixed by #16346
Closed

GraalVM Native Image Requires Config for lazy vals #13985

jamesward opened this issue Nov 21, 2021 · 28 comments · Fixed by #16346

Comments

@jamesward
Copy link

Compiler version

3.1.0

Background

GraalVM Native Image Ahead-of-Time compiles JVM apps into native executables. To do the AOT compilation, reflection points must be specified.

Issue 1

Scala's scala.runtime.LazyVals uses reflection so it must be declared. This could be done in the Scala stdlib so that others don't need to do it themselves. But this approach is a bit brittle. Any chance of removing the use of sun.misc.Unsafe there? If not, then I can send a PR to include the GraalVM reflection config:

  {
    "name":"sun.misc.Unsafe",
    "allDeclaredFields":true
  }

Issue 2

Code generated by the compiler for lazy vals also uses reflection and must be declared, like:

  {
    "name":"App$package$",
    "fields":[{"name":"0bitmap$1"}]
  }

Also, ideally reflection is not required. But I haven't looked into what the reflection is doing. If someone uses a Scala library that uses lazy vals, and does NOT provide their reflection config for GraalVM, then the user must specify the config for the library on their own. Ideally the library authors nor the users need to specify this info.

Steps to Reproduce

  1. Clone https://github.com/jamesward/scala3-graalvm-tester.git
  2. Switch to the lazy-reflect-broke branch
  3. Run ./sbt nativeImage nativeImageRun

You will see a runtime error showing Issue 1:

Exception in thread "main" scala.runtime.LazyVals$$anon$1
	at scala.runtime.LazyVals$.$init$$$anonfun$3(LazyVals.scala:18)
	at scala.Option.getOrElse(Option.scala:201)
	at scala.runtime.LazyVals$.<clinit>(LazyVals.scala:19)
	at App$package$.<clinit>(App.scala:2)
	at run.main(App.scala:4)

(Note: It should be possible to fail the build in this case instead of producing a runtime error but there seems to be a bug: oracle/graal#4010)

If you switch to the lazy-reflect-works branch and re-run ./sbt nativeImage nativeImageRun you will see that it works given the requisite config for the app in that repo that has one lazy val.

One Approach (If Reflection Must Stay)

The Spring team has developed a set of annotations which provide the reflection info, like:
https://docs.spring.io/spring-native/docs/0.9.1-SNAPSHOT/api/org/springframework/nativex/hint/TypeHint.html

There has been talk that this work will merge in some way into GraalVM itself. In that case, the Scala compiler could generate the necessary TypeHint annotations for places it generates code that uses reflection.

@xuwei-k
Copy link
Contributor

xuwei-k commented Nov 22, 2021

@nicolasstucki
Copy link
Contributor

nicolasstucki commented Nov 22, 2021

This reflective call was added in 0f7d251 in PR #2827.

@michelou
Copy link
Contributor

Long standing issue that should have received more attention.
See for instance article (2015) Java’s Unsafe class: “There is certainly also a way without it!”.

@nicolasstucki nicolasstucki added help wanted Spree Suitable for a future Spree labels Nov 22, 2021
@smarter smarter added area:transform and removed Spree Suitable for a future Spree labels Jan 9, 2022
@francisdb
Copy link

Some more context here: https://www.reddit.com/r/scala/comments/rzvkmi/graalvm_nativeimage_with_scala_3_reflection_hell/

@TheElectronWill
Copy link
Contributor

I believe that this is fixed by #14780 (and the new scheme implemented by #15296 should keep the fix)

@Dispersia
Copy link

hello, i tried with scala 3.2.0-RC3 and it's still happening. I don't see what release that one is supposed to be a part of, but it was checked in much before that date, is there any way I can confirm if it's in by that one or not?

@sideeffffect
Copy link
Contributor

This fixing commit is present since 3.2.0-RC1.

@xuwei-k
Copy link
Contributor

xuwei-k commented Aug 14, 2022

Maybe need re-compile with new Scala 3 version all libraries 🤔

@jamesward
Copy link
Author

I bumped the https://github.com/jamesward/scala3-graalvm-tester.git branch lazy-reflect-broke to 3.2.0-RC3 and still have the issue.

@Dispersia
Copy link

As @jamesward example, I have no libraries, it's happening from just having a lazy in my own code

@smarter
Copy link
Member

smarter commented Aug 15, 2022

Let's re-open the issue then, maybe @romanowski could have a look since he implemented the original fix in #14780 ?

@smarter smarter reopened this Aug 15, 2022
@smarter smarter modified the milestones: 3.2.1-RC1, 3.3.0-RC1 Aug 15, 2022
@szymon-rd szymon-rd self-assigned this Aug 16, 2022
@szymon-rd
Copy link
Contributor

szymon-rd commented Aug 16, 2022

It still breaks because we still use reflection to acquire Unsafe instance. It does not need the bitmap field anymore, as it is hardcoded for reflection and Graal can deal with that. However, the code of scala.runtime.LazyVals does this:

classOf[sun.misc.Unsafe].getDeclaredFields.nn.find { field =>
        field.nn.getType == classOf[sun.misc.Unsafe] && {
          field.nn.setAccessible(true)
          true
        }
      }

If we accessed the Unsafe field theUnsafe with a constant name, then I think it should work. It would be a small trade-off, at there is no guarantee for it to be named this way. However, if there are any other Unsafe implementations with different names, we can just hardcode them as well as fallbacks. I am also not a hundred percent sure that it would work, I don't know how the Unsafe is provided to the Graal image.

@TheElectronWill
Copy link
Contributor

IMO the best solution is to stop using sun.misc.Unsafe on Java 9+, i.e. fixing #9013.

This will fix GraalVM problems because it's only Java 11 and Java 17.

@smarter
Copy link
Member

smarter commented Aug 16, 2022

That would help too, but it won't completely solve the issue until Scala libraries that use lazy vals stop supporting Java 8.

@TheElectronWill
Copy link
Contributor

If we can't take the decision at runtime (java 8 vs java 9+) then yes :(

carlosedp added a commit to carlosedp/zio-scalajs-stack that referenced this issue Oct 14, 2022
carlosedp added a commit to carlosedp/zio-scalajs-stack that referenced this issue Oct 14, 2022
carlosedp added a commit to carlosedp/zio-scalajs-stack that referenced this issue Oct 14, 2022
@carlosedp
Copy link

carlosedp commented Oct 17, 2022

I have a sample project using ZIO and Scala 3 and this issue happens. Running the Native Image binary gave me:

❯ /Users/cdepaula/repos/scala-playground/zio-scalajs-stack/out/backend/nativeImage.dest/backend
Exception in thread "main" scala.runtime.LazyVals$$anon$1
	at scala.runtime.LazyVals$.$init$$$anonfun$3(LazyVals.scala:18)
	at scala.Option.getOrElse(Option.scala:201)
	at scala.runtime.LazyVals$.<clinit>(LazyVals.scala:19)
	at zio.ZLayer.<clinit>(ZLayer.scala:42)
	at java.lang.Class.ensureInitialized(DynamicHub.java:525)
	at zio.ZLayer$Suspend$.apply(ZLayer.scala:437)
	at zio.ZLayer$.suspend(ZLayer.scala:667)
	at zio.ZLayer$.fromZIOEnvironment(ZLayer.scala:613)
	at zio.ZLayer$.<clinit>(ZLayer.scala:558)
	at zio.ZIOAppDefault.$init$(ZIOAppDefault.scala:43)
	at com.carlosedp.zioscalajs.backend.MainApp$.<clinit>(MainApp.scala:11)
	at com.carlosedp.zioscalajs.backend.MainApp.main(MainApp.scala)

I tried @jamesward workaround by creating /backend/resources/META-INF/native-image/reflect-config.json with:

[
  {
    "name":"sun.misc.Unsafe",
    "allDeclaredFields":true
  },
  {
    "name":"com.carlosedp.zioscalajs.backend.MainApp$package$",
    "fields":[{"name":"0bitmap$1"}]
  }
]

and then had the error:

❯ /Users/cdepaula/repos/scala-playground/zio-scalajs-stack/out/backend/nativeImage.dest/backend
Exception in thread "main" java.lang.ExceptionInInitializerError
	at java.lang.Class.ensureInitialized(DynamicHub.java:525)
	at zio.ZLayer$Suspend$.apply(ZLayer.scala:437)
	at zio.ZLayer$.suspend(ZLayer.scala:667)
	at zio.ZLayer$.fromZIOEnvironment(ZLayer.scala:613)
	at zio.ZLayer$.<clinit>(ZLayer.scala:558)
	at zio.ZIOAppDefault.$init$(ZIOAppDefault.scala:43)
	at com.carlosedp.zioscalajs.backend.MainApp$.<clinit>(MainApp.scala:11)
	at com.carlosedp.zioscalajs.backend.MainApp.main(MainApp.scala)
Caused by: java.lang.NoSuchFieldException: 0bitmap$1
	at java.lang.Class.getDeclaredField(DynamicHub.java:968)
	at scala.runtime.LazyVals$.getOffset(LazyVals.scala:103)
	at zio.ZLayer.<clinit>(ZLayer.scala:42)
	... 8 more

Also tried removing the 0bitmap$1 field but same happens. I'm using Scala 3.2.0. Anything I missed?

@carlosedp
Copy link

carlosedp commented Nov 14, 2022

Tried this with latest 3.2.2-RC1 (which merged #15296) by enabling -Ylightweight-lazy-vals but still got the same error on my project.

Also tested @jamesward sample repo and branch and same error happens.

Do I need something else?

@smarter
Copy link
Member

smarter commented Nov 14, 2022

It seems there's still something missing, @szymon-rd in #13985 (comment) suggested we need to go through theUnsafe to get the unsafe instance, has anyone tried that?

@szymon-rd
Copy link
Contributor

@carlosedp
Could you check if #16346 fixes the issue for you?

@carlosedp
Copy link

Thanks @szymon-rd I'm out today on local holy day returning tomorrow afternoon. Gonna test as soon as I get in computer.

@carlosedp
Copy link

carlosedp commented Nov 16, 2022

Hey @szymon-rd , thanks a lot for the PR. I tried to go as deep as possible on this since the sample from above (simple LazyVal) worked but my sample app in Zio didn't.

I went on adding the unsafe fields to reflect-config.json for each error I found but stopped since it went on and on.

One thing in common is that all of them refers to the getOffset method from LazyVals.scala so seems some fix on it might fix it all.

Below the reflect-config:

Click me
[
    {
        "name": "izumi.reflect.macrortti.LightTypeTag",
        "fields": [
            {
                "name": "0bitmap$1",
                "allowUnsafeAccess": true
            }
        ]
    },
    {
        "name": "izumi.reflect.macrortti.LightTypeTag$ParsedLightTypeTag210",
        "fields": [
            {
                "name": "0bitmap$5",
                "allowUnsafeAccess": true
            }
        ]
    },
    {
        "name": "zhttp.http.Method$PUT$",
        "fields": [
            {
                "name": "0bitmap$6",
                "allowUnsafeAccess": true
            }
        ]
    },
    {
        "name": "zhttp.http.Method$DELETE$",
        "fields": [
            {
                "name": "0bitmap$8",
                "allowUnsafeAccess": true
            }
        ]
    },
    {
        "name": "zhttp.http.Method$POST$",
        "fields": [
            {
                "name": "0bitmap$5",
                "allowUnsafeAccess": true
            }
        ]
    },
    {
        "name": "zhttp.http.Method$GET$",
        "fields": [
            {
                "name": "0bitmap$3",
                "allowUnsafeAccess": true
            }
        ]
    },
    {
        "name": "izumi.reflect.macrortti.LightTypeTagRef$NameReference",
        "fields": [
            {
                "name": "0bitmap$5",
                "allowUnsafeAccess": true
            }
        ]
    },
    {
        "name": "izumi.reflect.macrortti.LightTypeTagInheritance",
        "fields": [
            {
                "name": "0bitmap$1",
                "allowUnsafeAccess": true
            }
        ]
    },
    {
        "name": "izumi.reflect.internal.fundamentals.platform.console.TrivialLogger$Config",
        "fields": [
            {
                "name": "0bitmap$1",
                "allowUnsafeAccess": true
            }
        ]
    }
]

And the errors I found each execution:

Click me
Exception in thread "main" java.lang.ExceptionInInitializerError
	at java.base@17.0.5/java.lang.Class.ensureInitialized(DynamicHub.java:528)
	at izumi.reflect.macrortti.LightTypeTag$.parse(LightTypeTag.scala:271)
	at zio.ZIOAppDefault.$init$(ZIOAppDefault.scala:45)
	at com.carlosedp.zioscalajs.backend.MainApp$.<clinit>(MainApp.scala:10)
	at com.carlosedp.zioscalajs.backend.MainApp.main(MainApp.scala)
Caused by: java.lang.NoSuchFieldException: 0bitmap$1
	at java.base@17.0.5/java.lang.Class.getDeclaredField(DynamicHub.java:961)
	at scala.runtime.LazyVals$.getOffset(LazyVals.scala:138)
	at izumi.reflect.macrortti.LightTypeTag.<clinit>(LightTypeTag.scala:49)
	... 5 more

--------------------------------------------------------------------------------
Exception in thread "main" java.lang.ExceptionInInitializerError
	at izumi.reflect.macrortti.LightTypeTag$.parse(LightTypeTag.scala:271)
	at zio.ZIOAppDefault.$init$(ZIOAppDefault.scala:45)
	at com.carlosedp.zioscalajs.backend.MainApp$.<clinit>(MainApp.scala:10)
	at com.carlosedp.zioscalajs.backend.MainApp.main(MainApp.scala)
Caused by: java.lang.NoSuchFieldException: 0bitmap$5
	at java.base@17.0.5/java.lang.Class.getDeclaredField(DynamicHub.java:961)
	at scala.runtime.LazyVals$.getOffset(LazyVals.scala:138)
	at izumi.reflect.macrortti.LightTypeTag$ParsedLightTypeTag210.<clinit>(LightTypeTag.scala:375)
	... 4 more

--------------------------------------------------------------------------------
Exception in thread "main" java.lang.ExceptionInInitializerError
	at com.carlosedp.zioscalajs.backend.MainApp$.<clinit>(MainApp.scala:15)
	at com.carlosedp.zioscalajs.backend.MainApp.main(MainApp.scala)
Caused by: java.lang.NoSuchFieldException: 0bitmap$6
	at java.base@17.0.5/java.lang.Class.getDeclaredField(DynamicHub.java:961)
	at scala.runtime.LazyVals$.getOffset(LazyVals.scala:138)
	at zhttp.http.Method$PUT$.<clinit>(Method.scala:58)
	... 2 more

--------------------------------------------------------------------------------
Exception in thread "main" java.lang.ExceptionInInitializerError
	at com.carlosedp.zioscalajs.backend.MainApp$.<clinit>(MainApp.scala:15)
	at com.carlosedp.zioscalajs.backend.MainApp.main(MainApp.scala)
Caused by: java.lang.NoSuchFieldException: 0bitmap$8
	at java.base@17.0.5/java.lang.Class.getDeclaredField(DynamicHub.java:961)
	at scala.runtime.LazyVals$.getOffset(LazyVals.scala:138)
	at zhttp.http.Method$DELETE$.<clinit>(Method.scala:60)
	... 2 more

--------------------------------------------------------------------------------
Exception in thread "main" java.lang.ExceptionInInitializerError
	at com.carlosedp.zioscalajs.backend.MainApp$.<clinit>(MainApp.scala:15)
	at com.carlosedp.zioscalajs.backend.MainApp.main(MainApp.scala)
Caused by: java.lang.NoSuchFieldException: 0bitmap$5
	at java.base@17.0.5/java.lang.Class.getDeclaredField(DynamicHub.java:961)
	at scala.runtime.LazyVals$.getOffset(LazyVals.scala:138)
	at zhttp.http.Method$POST$.<clinit>(Method.scala:57)
	... 2 more

--------------------------------------------------------------------------------
Exception in thread "main" java.lang.ExceptionInInitializerError
	at com.carlosedp.zioscalajs.backend.MainApp$.<clinit>(MainApp.scala:15)
	at com.carlosedp.zioscalajs.backend.MainApp.main(MainApp.scala)
Caused by: java.lang.NoSuchFieldException: 0bitmap$3
	at java.base@17.0.5/java.lang.Class.getDeclaredField(DynamicHub.java:961)
	at scala.runtime.LazyVals$.getOffset(LazyVals.scala:138)
	at zhttp.http.Method$GET$.<clinit>(Method.scala:55)
	... 2 more

--------------------------------------------------------------------------------
timestamp=2022-11-16T18:37:56.009881Z level=ERROR thread=#zio-fiber-0 message="" cause="Exception in thread "zio-fiber-4" java.lang.ExceptionInInitializerError: null
	at izumi.reflect.macrortti.LightTypeTag$$anon$8.unpickle(LightTypeTag.scala:490)
	at izumi.reflect.macrortti.LightTypeTag$$anon$8.unpickle(LightTypeTag.scala:487)
	at izumi.reflect.thirdparty.internal.boopickle.CompositePickler.unpickle(CompositePicklers.scala:56)
	at izumi.reflect.macrortti.LightTypeTag$.deserializeRefString(LightTypeTag.scala:395)
	at izumi.reflect.macrortti.LightTypeTag$ParsedLightTypeTag210.ref(LightTypeTag.scala:377)
	at izumi.reflect.macrortti.LightTypeTag.decompose(LightTypeTag.scala:69)
	at zio.VersionSpecific.taggedGetServices(VersionSpecific.scala:75)
	at zio.VersionSpecific.taggedGetServices$(VersionSpecific.scala:26)
	at zio.package$.taggedGetServices(package.scala:23)
	at zio.ZEnvironment.prune(ZEnvironment.scala:65)
	at zio.ZEnvironment.union(ZEnvironment.scala:95)
	at zio.Scope$ExtendPartiallyApplied$.apply$extension$$anonfun$1(Scope.scala:172)
	at zio.ZIO.provideSomeEnvironment$$anonfun$1$$anonfun$1(ZIO.scala:1240)
	at zio.ZIO.provideEnvironment(ZIO.scala:1220)
	at zio.ZIO.provideEnvironment$(ZIO.scala:52)
	at zio.ZIO$OnSuccess.provideEnvironment(ZIO.scala:5533)
	at zio.ZIO.provideSomeEnvironment$$anonfun$1(ZIO.scala:1240)
	Suppressed: java.lang.NoSuchFieldException: 0bitmap$5
		at java.base@17.0.5/java.lang.Class.getDeclaredField(DynamicHub.java:961)
		at scala.runtime.LazyVals$.getOffset(LazyVals.scala:138)
		at izumi.reflect.macrortti.LightTypeTagRef$NameReference.<clinit>(LightTypeTagRef.scala:343)
		at izumi.reflect.macrortti.LightTypeTag$$anon$8.unpickle(LightTypeTag.scala:490)
		at izumi.reflect.macrortti.LightTypeTag$$anon$8.unpickle(LightTypeTag.scala:487)
		at izumi.reflect.thirdparty.internal.boopickle.CompositePickler.unpickle(CompositePicklers.scala:56)
		at izumi.reflect.macrortti.LightTypeTag$.deserializeRefString(LightTypeTag.scala:395)
		at izumi.reflect.macrortti.LightTypeTag$ParsedLightTypeTag210.ref(LightTypeTag.scala:377)
		at izumi.reflect.macrortti.LightTypeTag.decompose(LightTypeTag.scala:69)
		at zio.VersionSpecific.taggedGetServices(VersionSpecific.scala:75)
		at zio.VersionSpecific.taggedGetServices$(VersionSpecific.scala:26)
		at zio.package$.taggedGetServices(package.scala:23)
		at zio.ZEnvironment.prune(ZEnvironment.scala:65)
		at zio.ZEnvironment.union(ZEnvironment.scala:95)
		at zio.Scope$ExtendPartiallyApplied$.apply$extension$$anonfun$1(Scope.scala:172)
		at zio.ZIO.provideSomeEnvironment$$anonfun$1$$anonfun$1(ZIO.scala:1240)
		at zio.ZIO.provideEnvironment(ZIO.scala:1220)
		at zio.ZIO.provideEnvironment$(ZIO.scala:52)
		at zio.ZIO$OnSuccess.provideEnvironment(ZIO.scala:5533)
		at zio.ZIO.provideSomeEnvironment$$anonfun$1(ZIO.scala:1240)"

--------------------------------------------------------------------------------
timestamp=2022-11-16T18:43:25.741961Z level=ERROR thread=#zio-fiber-0 message="" cause="Exception in thread "zio-fiber-4" java.lang.ExceptionInInitializerError: null
	at izumi.reflect.internal.fundamentals.platform.console.TrivialLogger$Config$.console(TrivialLogger.scala:71)
	at izumi.reflect.macrortti.LightTypeTagInheritance.isChild(LightTypeTagInheritance.scala:61)
	at izumi.reflect.macrortti.LightTypeTag.$less$colon$less(LightTypeTag.scala:61)
	at zio.VersionSpecific.taggedIsSubtype$$anonfun$1(VersionSpecific.scala:63)
	at java.base@17.0.5/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
	at zio.VersionSpecific.taggedIsSubtype(VersionSpecific.scala:63)
	at zio.VersionSpecific.taggedIsSubtype$(VersionSpecific.scala:26)
	at zio.package$.taggedIsSubtype(package.scala:23)
	at zio.ZEnvironment.$anonfun$1$$anonfun$1(ZEnvironment.scala:68)
	at scala.collection.IterableOnceOps.exists(IterableOnce.scala:603)
	at scala.collection.IterableOnceOps.exists$(IterableOnce.scala:600)
	at scala.collection.AbstractIterable.exists(Iterable.scala:933)
	at zio.ZEnvironment.$anonfun$1(ZEnvironment.scala:68)
	at scala.collection.immutable.Set$Set1.filterImpl(Set.scala:172)
	at scala.collection.immutable.Set$Set1.filterImpl(Set.scala:156)
	at scala.collection.StrictOptimizedIterableOps.filterNot(StrictOptimizedIterableOps.scala:220)
	at scala.collection.StrictOptimizedIterableOps.filterNot$(StrictOptimizedIterableOps.scala:220)
	at scala.collection.immutable.Set$Set1.filterNot(Set.scala:156)
	at zio.ZEnvironment.prune(ZEnvironment.scala:68)
	at zio.ZEnvironment.union(ZEnvironment.scala:95)
	at zio.Scope$ExtendPartiallyApplied$.apply$extension$$anonfun$1(Scope.scala:172)
	at zio.ZIO.provideSomeEnvironment$$anonfun$1$$anonfun$1(ZIO.scala:1240)
	at zio.ZIO.provideEnvironment(ZIO.scala:1220)
	at zio.ZIO.provideEnvironment$(ZIO.scala:52)
	at zio.ZIO$OnSuccess.provideEnvironment(ZIO.scala:5533)
	at zio.ZIO.provideSomeEnvironment$$anonfun$1(ZIO.scala:1240)
	Suppressed: java.lang.NoSuchFieldException: 0bitmap$1
		at java.base@17.0.5/java.lang.Class.getDeclaredField(DynamicHub.java:961)
		at scala.runtime.LazyVals$.getOffset(LazyVals.scala:138)
		at izumi.reflect.internal.fundamentals.platform.console.TrivialLogger$Config.<clinit>(TrivialLogger.scala:71)
		at izumi.reflect.internal.fundamentals.platform.console.TrivialLogger$Config$.console(TrivialLogger.scala:71)
		at izumi.reflect.macrortti.LightTypeTagInheritance.isChild(LightTypeTagInheritance.scala:61)
		at izumi.reflect.macrortti.LightTypeTag.$less$colon$less(LightTypeTag.scala:61)
		at zio.VersionSpecific.taggedIsSubtype$$anonfun$1(VersionSpecific.scala:63)
		at java.base@17.0.5/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
		at zio.VersionSpecific.taggedIsSubtype(VersionSpecific.scala:63)
		at zio.VersionSpecific.taggedIsSubtype$(VersionSpecific.scala:26)
		at zio.package$.taggedIsSubtype(package.scala:23)
		at zio.ZEnvironment.$anonfun$1$$anonfun$1(ZEnvironment.scala:68)
		at scala.collection.IterableOnceOps.exists(IterableOnce.scala:603)
		at scala.collection.IterableOnceOps.exists$(IterableOnce.scala:600)
		at scala.collection.AbstractIterable.exists(Iterable.scala:933)
		at zio.ZEnvironment.$anonfun$1(ZEnvironment.scala:68)
		at scala.collection.immutable.Set$Set1.filterImpl(Set.scala:172)
		at scala.collection.immutable.Set$Set1.filterImpl(Set.scala:156)
		at scala.collection.StrictOptimizedIterableOps.filterNot(StrictOptimizedIterableOps.scala:220)
		at scala.collection.StrictOptimizedIterableOps.filterNot$(StrictOptimizedIterableOps.scala:220)
		at scala.collection.immutable.Set$Set1.filterNot(Set.scala:156)
		at zio.ZEnvironment.prune(ZEnvironment.scala:68)
		at zio.ZEnvironment.union(ZEnvironment.scala:95)
		at zio.Scope$ExtendPartiallyApplied$.apply$extension$$anonfun$1(Scope.scala:172)
		at zio.ZIO.provideSomeEnvironment$$anonfun$1$$anonfun$1(ZIO.scala:1240)
		at zio.ZIO.provideEnvironment(ZIO.scala:1220)
		at zio.ZIO.provideEnvironment$(ZIO.scala:52)
		at zio.ZIO$OnSuccess.provideEnvironment(ZIO.scala:5533)
		at zio.ZIO.provideSomeEnvironment$$anonfun$1(ZIO.scala:1240)"

The project I've been testing on is at https://github.com/carlosedp/zio-scalajs-stack and it's configured to build with GraalVM and Scala 2.13 here https://github.com/carlosedp/zio-scalajs-stack/blob/main/build.sc#L57. I build with ./mill show backend.nativeImage.

Let me know if I can help by providing any additional info.
Hope it helps.

@szymon-rd
Copy link
Contributor

szymon-rd commented Nov 17, 2022

@carlosedp I think it is because these libraries were not compiled with the newer lazy vals. My PR should fix initialization of LazyVals runtime class. However, I don't think that we are able to change the runtime to make the libraries compiled with older Scala run without these exceptions. The bytecode of the libraries contains code that cannot be run without config. There are some solutions that can be implemented to make it work (any of these steps should help):

  • compile library with new Scala compiler
  • make an utility that traverses bytecode and extracts these bitmap fields and prepares the config
  • make an utility that modifies bytecode in libraries to avoid using the old approach (would solve the problem, may even be the easiest one, but a bit hacky)

@carlosedp
Copy link

Thanks for the feedback Szymon. I don't think I have the knowledge to attempt solutions 2 and 3 so I'll try to test building the libs locally with new compiler and wait until they get published to have it upstream.

Glad to know it should work from now on. Thanks a lot!

@makingthematrix
Copy link
Contributor

I can confirm it works for my small Scala 3 + JavaFX project. Thanks! <3
https://github.com/makingthematrix/scalaonandroid/tree/main/calculator3

@carlosedp
Copy link

An FYI, scala-cli does one of the options mentioned by @szymon-rd. It manipulates the bytecode and patches the jars to "fix" lazyvals on current libraries.

https://github.com/VirtusLab/scala-cli/tree/main/modules/scala3-graal/src/main/scala/scala/cli/graal

@carlosedp
Copy link

I'm happy to add that since using Scala 3.3 (3.3.0-RC2), my ZIO project works perfectly with NativeImage.
Again thanks @szymon-rd for this!

@szymon-rd
Copy link
Contributor

szymon-rd commented Feb 2, 2023

@carlosedp 3.3.0-RC2 has a bug for GraalVM, it sadly occurs fairly commonly and crashes the whole image. We will be backporting the fix for it: #16800
However, you can add -Ylegacy-lazy-vals option, the native image is also fixed in the legacy version.

@jamesward
Copy link
Author

Thanks @szymon-rd! I've verified this fix: https://github.com/jamesward/scala3-graalvm-tester/tree/lazy-reflect-fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.