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

Add ClassValueCompat to support systems without java.lang.ClassValue (such as Android) #9752

Merged
merged 1 commit into from Oct 12, 2021

Conversation

nwk37011
Copy link
Contributor

@nwk37011 nwk37011 commented Sep 7, 2021

The goal of this commit is to enable systems without java.lang.ClassValue to fall back to other method which does not utilize java.lang.ClassValue while keeping its current behavior as how it is for systems with java.lang.ClassValue. Use of java.lang.ClassValue seems mainly for caching as #7879 introduced, so I ran a related benchmark from one of the commits and got following results

Baseline

[info] Benchmark                                             Mode  Cnt   Score    Error   Units
[info] ClassTagBenchmark.lookupClassTag                      avgt   20  40.328 ±  0.496   ns/op
[info] ClassTagBenchmark.lookupClassTag:·gc.alloc.rate       avgt   20  ≈ 10⁻⁴           MB/sec
[info] ClassTagBenchmark.lookupClassTag:·gc.alloc.rate.norm  avgt   20  ≈ 10⁻⁵             B/op
[info] ClassTagBenchmark.lookupClassTag:·gc.count            avgt   20     ≈ 0           counts
ClassValueCompat - java.lang.ClassValue exists

[info] Benchmark                                             Mode  Cnt   Score    Error   Units
[info] ClassTagBenchmark.lookupClassTag                      avgt   20  42.834 ±  0.231   ns/op
[info] ClassTagBenchmark.lookupClassTag:·gc.alloc.rate       avgt   20  ≈ 10⁻⁴           MB/sec
[info] ClassTagBenchmark.lookupClassTag:·gc.alloc.rate.norm  avgt   20  ≈ 10⁻⁵             B/op
[info] ClassTagBenchmark.lookupClassTag:·gc.count            avgt   20     ≈ 0           counts

For cases where systems lacking java.lang.ClassValue, I earned the following result which seems obviously slower than the above two but still it enables such systems to actually utilize classes such as ClassTag instead of throwing ClassNotFoundException

ClassValueCompat - java.lang.ClassValue does not exist

[info] Benchmark                                                      Mode  Cnt     Score    Error   Units
[info] ClassTagBenchmark.lookupClassTag                               avgt   20    79.452 ±  0.294   ns/op
[info] ClassTagBenchmark.lookupClassTag:·gc.alloc.rate                avgt   20  3838.471 ± 15.265  MB/sec
[info] ClassTagBenchmark.lookupClassTag:·gc.alloc.rate.norm           avgt   20   336.000 ±  0.001    B/op
[info] ClassTagBenchmark.lookupClassTag:·gc.churn.G1_Eden_Space       avgt   20  3843.122 ± 24.785  MB/sec
[info] ClassTagBenchmark.lookupClassTag:·gc.churn.G1_Eden_Space.norm  avgt   20   336.408 ±  1.792    B/op
[info] ClassTagBenchmark.lookupClassTag:·gc.churn.G1_Old_Gen          avgt   20     0.003 ±  0.001  MB/sec
[info] ClassTagBenchmark.lookupClassTag:·gc.churn.G1_Old_Gen.norm     avgt   20    ≈ 10⁻⁴             B/op
[info] ClassTagBenchmark.lookupClassTag:·gc.count                     avgt   20  1525.000           counts
[info] ClassTagBenchmark.lookupClassTag:·gc.time                      avgt   20   655.000               ms

This commit combined with #9739 will open Scala 2.13.x to wider range of systems including Android as discussed and demonstrated at https://users.scala-lang.org/t/scala-2-13-on-android/7235/24

@smarter
Copy link
Member

smarter commented Sep 7, 2021

Nice! FWIW, I've also opened an issue against Android about the lack of ClassValue, but it's not clear to me if they're interested in fixing it: https://issuetracker.google.com/issues/196063118 (maybe someone with some twitter clout could try bringing attention to this?)

@SethTisue SethTisue added the release-notes worth highlighting in next release notes label Sep 7, 2021
Copy link
Member

@lrytz lrytz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for that contribution, the approach LGTM!

src/library/scala/runtime/ClassValueCompat.java Outdated Show resolved Hide resolved
src/library/scala/runtime/ClassValueCompat.java Outdated Show resolved Hide resolved
src/library/scala/runtime/ClassValueCompat.java Outdated Show resolved Hide resolved
src/library/scala/runtime/ClassValueCompat.java Outdated Show resolved Hide resolved
@nwk37011 nwk37011 force-pushed the classvaluecompat branch 3 times, most recently from c5a9d09 to 272907f Compare September 17, 2021 07:48
@SethTisue SethTisue marked this pull request as draft September 22, 2021 16:19
Copy link
Member

@lrytz lrytz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the rewrite of ModuleSerializationProxy in Scala looks good, but it's certainly not trivial to think of all the possible consequences.


import scala.util.Try

private[scala] abstract class ClassValueCompat[T] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to bring this to discussion again, but do we really want to add this class to the scala package? Even if it's package private it will show up in some places way too prominently. For example when editing a Java file in a mixed project, scala.ClassValueCompat will be a public class.

I'd rather keep it in scala.runtime.

Copy link
Member

@SethTisue SethTisue Sep 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree; I argued (at least somewhat) against scala.compat, but I think scala is an even worse choice

Copy link
Contributor Author

@nwk37011 nwk37011 Oct 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issues and fixes mentioned below and here got applied to the latest commit

src/library/scala/ClassValueCompat.scala Outdated Show resolved Hide resolved
src/library/scala/ClassValueCompat.scala Outdated Show resolved Hide resolved
src/library/scala/ClassValueCompat.scala Outdated Show resolved Hide resolved
src/library/scala/runtime/ModuleSerializationProxy.scala Outdated Show resolved Hide resolved
src/library/scala/runtime/ModuleSerializationProxy.scala Outdated Show resolved Hide resolved
@lrytz
Copy link
Member

lrytz commented Oct 7, 2021

I looked at the rewrite of ModuleSerializationProxy in Scala in more detail and it LGTM.

  • serializing a module (object O extends Serializable, which gets a synthetic def writeReplace() = new ModuleSerializationProxy(classOf[O$])) gives the exact same byte array on 2.13.6 and with this PR
  • serializing / deserializing the module back and forth between 2.13.6 and this PR works
  • classfile diff looks fine: https://gist.github.com/lrytz/c75bf89a3bfd7618ad575ea7c8e1c183

On runtime systems where `java.lang.ClassValue` is not available
`ClassValueCompat` re-computes the value on each `get` invocation.

Co-authored-by: nwk37011 <nwk37011@gmail.com>
@lrytz lrytz marked this pull request as ready for review October 12, 2021 08:32
Copy link
Member

@lrytz lrytz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebased and squashed.

Thank you, @nwk37011!

@lrytz lrytz enabled auto-merge October 12, 2021 08:38
def remove(cls: Class[_]): Unit
}

private val classValueAvailable: Boolean = try {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it in any way be beneficial to make it lazy val? 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, the field is only initialized when the enclosing object is first accessed.

@lrytz lrytz merged commit 12ff1e3 into scala:2.13.x Oct 12, 2021
SethTisue added a commit to SethTisue/scala that referenced this pull request Oct 12, 2021
@SethTisue
Copy link
Member

on JDK 17 we're seeing (at https://app.travis-ci.com/github/scala/scala/jobs/542786102):

[error] /home/travis/build/scala/scala/src/library/scala/runtime/ModuleSerializationProxy.scala:23:23: class AccessController in package security is deprecated
[error]         java.security.AccessController.doPrivileged((() => cls.getField("MODULE$").get(null)): PrivilegedExceptionAction[Object])
[error]                       ^

PR with fix: #9785

@SethTisue SethTisue changed the title ClassValueCompat to support systems without java.lang.ClassValue Add ClassValueCompat to support systems without java.lang.ClassValue Oct 29, 2021
@SethTisue SethTisue changed the title Add ClassValueCompat to support systems without java.lang.ClassValue Add ClassValueCompat to support systems without java.lang.ClassValue (such as Android) Oct 29, 2021
@fiatjaf
Copy link

fiatjaf commented Jul 26, 2022

I have a question related to this PR, so I thought I would just beg for attention here. Let me know if this is inappropriate: https://stackoverflow.com/questions/73042201/why-a-scala-2-13-app-is-trying-to-use-javaclassvalue-on-android-if-it-is-not-a

sideeffffect referenced this pull request in chenakam/scalroid Feb 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release-notes worth highlighting in next release notes
Projects
None yet