8000 WiP Separate the scalalib from the Scala.js library. by sjrd · Pull Request #4787 · scala-js/scala-js · GitHub
[go: up one dir, main page]

Skip to content

WiP Separate the scalalib from the Scala.js library. #4787

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

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Apply the IR cleaner to the scalalib and the library-aux.
It is configured differently than for the javalib. We allow
references to `scala.*` (obviously) but disallow references to
`scala.scalajs.*`. We also avoid rewriting Scala functions to JS
functions, since that changes the binary APIs.

We use a local copy of `sjs.runtime.AnonFunctionN` classes in
`scala.runtime.*` to support the codegen of lambdas.
  • Loading branch information
sjrd committed Oct 27, 2023
commit b35d481955732659ec95e1782c539b15be83eb0c
Original file line number Diff line number Diff line change
Expand Up @@ -5906,6 +5906,8 @@ private[optimizer] object OptimizerCore {
),
ClassName("scala.collection.mutable.ArrayBuilder$") -> List(
m("scala$collection$mutable$ArrayBuilder$$zeroOf", List(ClassClassRef), O) -> ArrayBuilderZeroOf,
m("scala$collection$mutable$ArrayBuilder$$genericArrayBuilderResult", List(ClassClassRef, O), O) -> GenericArrayBuilderResult,
// and its legacy signature (< v1.13.0)
m("scala$collection$mutable$ArrayBuilder$$genericArrayBuilderResult", List(ClassClassRef, JSArrayClassRef), O) -> GenericArrayBuilderResult
),
ClassName("java.lang.Class") -> List(
Expand Down
18 changes: 11 additions & 7 deletions project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -673,9 +673,9 @@ object Build {
}
)

val cleanIRSettings = Def.settings(
def cleanIRSettings(forScalalib: Boolean): Seq[Setting[_]] = Def.settings(
// In order to rewrite anonymous functions and tuples, the code must not be specialized
scalacOptions += "-no-specialization",
scalacOptions ++= (if (forScalalib) Nil else Seq("-no-specialization")),

products in Compile := {
val s = streams.value
Expand All @@ -684,7 +684,7 @@ object Build {

val outputDir = crossTarget.value / "cleaned-classes"

val irCleaner = new JavalibIRCleaner((LocalRootProject / baseDirectory).value.toURI())
val irCleaner = new JavalibIRCleaner((LocalRootProject / baseDirectory).value.toURI(), forScalalib)

val libFileMappings = (PathFinder(prevProducts) ** "*.sjsir")
.pair(Path.rebase(prevProducts, outputDir))
Expand Down Expand Up @@ -1141,7 +1141,7 @@ object Build {
name := "Scala.js linker private library",
publishArtifact in Compile := false,
delambdafySetting,
cleanIRSettings
cleanIRSettings(forScalalib = false),
).withScalaJSCompiler2_12.withScalaJSJUnitPlugin2_12.dependsOnLibrary2_12.dependsOn(
jUnitRuntime.v2_12 % "test", testBridge.v2_12 % "test",
)
Expand Down Expand Up @@ -1457,7 +1457,7 @@ object Build {
Seq(output)
}.taskValue,

cleanIRSettings,
cleanIRSettings(forScalalib = false),

Compile / doc := {
val dir = (Compile / doc / target).value
Expand Down Expand Up @@ -1654,6 +1654,8 @@ object Build {

headerSources in Compile := Nil,
headerSources in Test := Nil,

cleanIRSettings(forScalalib = true),
).withScalaJSCompiler.dependsOnLibraryNoJar

lazy val libraryAux: MultiScalaProject = MultiScalaProject(
Expand All @@ -1669,6 +1671,8 @@ object Build {
delambdafySetting,

recompileAllOrNothingSettings,

cleanIRSettings(forScalalib = true),
).withScalaJSCompiler.dependsOnLibraryNoJar

lazy val library: MultiScalaProject = MultiScalaProject(
Expand Down Expand Up @@ -1925,15 +1929,15 @@ object Build {
scalaVersion.value match {
case `default212Version` =>
Some(ExpectedSizes(
fastLink = 766000 to 767000,
fastLink = 767000 to 768000,
fullLink = 144000 to 145000,
fastLinkGz = 90000 to 91000,
fullLinkGz = 35000 to 36000,
))

case `default213Version` =>
Some(ExpectedSizes(
fastLink = 482000 to 483000,
fastLink = 483000 to 484000,
fullLink = 103000 to 104000,
fastLinkGz = 62000 to 63000,
fullLinkGz = 27000 to 28000,
Expand Down
70 changes: 52 additions & 18 deletions project/JavalibIRCleaner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@ import sbt.{Logger, MessageOnlyException}
* Afterwards, we check that the IR does not contain any reference to classes
* under the `scala.*` package.
*/
final class JavalibIRCleaner(baseDirectoryURI: URI) {
final class JavalibIRCleaner(baseDirectoryURI: URI, forScalalib: Boolean) {
import JavalibIRCleaner._

private val classNameSubstitutions: Map[ClassName, ClassName] =
if (forScalalib) ClassNameSubstitutionsForScalalib
else ClassNameSubstitutionsForJavalib

def cleanIR(dependencyFiles: Seq[File], libFileMappings: Seq[(File, File)],
logger: Logger): Set[File] = {

Expand Down Expand Up @@ -80,11 +84,19 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) {
}

if (errorManager.hasErrors) {
throw new MessageOnlyException(
s"There were ${errorManager.errorCount} errors while " +
"postprocessing the IR of the javalanglib. " +
"The javalanglib must be written in a style that does not leak any " +
"reference to the Scala library.")
if (forScalalib) {
throw new MessageOnlyException(
s"There were ${errorManager.errorCount} errors while " +
"postprocessing the IR of the scalalib. " +
"The scalalib must be written in a style that does not leak any " +
"reference to the Scala.js standard library.")
} else {
throw new MessageOnlyException(
s"There were ${errorManager.errorCount} errors while " +
"postprocessing the IR of the javalib. " +
"The javalib must be written in a style that does not leak any " +
"reference to the Scala library.")
}
}

resultBuilder.result()
Expand Down Expand Up @@ -146,10 +158,12 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) {
// Preprocess the super interface list
val newInterfaces = transformInterfaceList(interfaces)

/* Remove the `private def writeReplace__O` generated by scalac 2.13+
* in the companion of serializable classes.
/* javalib only: Remove the `private def writeReplace__O` generated by
* scalac 2.13+ in the companion of serializable classes.
*/
val newMethods = methods.filter(_.name.name != writeReplaceMethodName)
val newMethods =
if (forScalalib) methods
else methods.filter(_.name.name != writeReplaceMethodName)

val preprocessedTree = ClassDef(name, originalName, kind, jsClassCaptures,
superClass, newInterfaces, jsSuperClass, jsNativeLoadSpec, fields,
Expand Down Expand Up @@ -177,9 +191,13 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) {
/* Replace references to scala.Serializable by java.io.Serializable.
* This works around the fact that scalac adds scala.Serializable to the
* companion object of any class that extends java.io.Serializable.
*
* Don't do it for the scalalib.
*/

if (!interfaces.exists(_.name == ScalaSerializable)) {
if (forScalalib) {
interfaces
} else if (!interfaces.exists(_.name == ScalaSerializable)) {
interfaces
} else if (interfaces.exists(_.name == JavaIOSerializable)) {
interfaces.filter(_.name != ScalaSerializable)
Expand Down Expand Up @@ -321,13 +339,13 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) {
implicit val pos = tree.pos

tree match {
// new AnonFunctionN(closure) --> closure
case New(AnonFunctionNClass(n), _, List(closure)) =>
// javalib only: new AnonFunctionN(closure) --> closure
case New(AnonFunctionNClass(n), _, List(closure)) if !forScalalib =>
closure

// someFunctionN.apply(args) --> someFunctionN(args)
// javalib only: someFunctionN.apply(args) --> someFunctionN(args)
case Apply(ApplyFlags.empty, fun, MethodIdent(FunctionApplyMethodName(n)), args)
if isFunctionNType(n, fun.tpe) =>
if !forScalalib && isFunctionNType(n, fun.tpe) =>
JSFunctionApply(fun, args)

// <= 2.12 : toJSVarArgs(jsArrayOps(jsArray).toSeq) -> jsArray
Expand Down Expand Up @@ -582,7 +600,7 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) {
}

private def transformClassName(cls: ClassName)(implicit pos: Position): ClassName = {
ClassNameSubstitutions.getOrElse(cls, {
classNameSubstitutions.getOrElse(cls, {
validateClassName(cls)
cls
})
Expand All @@ -600,8 +618,13 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) {
def isAnException: Boolean =
isJavaScriptExceptionWithinItself || isTypedArrayBufferBridgeWithinItself

if (cls.nameString.startsWith("scala.") && !isAnException)
reportError(s"Illegal reference to Scala class ${cls.nameString}")
if (forScalalib) {
if (cls.nameString.startsWith("scala.scalajs."))
reportError(s"Illegal reference to Scala.js class ${cls.nameString}")
} else {
if (cls.nameString.startsWith("scala.") && !isAnException)
reportError(s"Illegal reference to Scala class ${cls.nameString}")
}
}

private def transformNonJSClassName(cls: ClassName)(implicit pos: Position): ClassName = {
Expand Down Expand Up @@ -729,7 +752,7 @@ object JavalibIRCleaner {
false
}

private val ClassNameSubstitutions: Map[ClassName, ClassName] = {
private val ClassNameSubstitutionsForJavalib: Map[ClassName, ClassName] = {
val functionTypePairs = for {
funClass <- FunctionNClasses ++ AnonFunctionNClasses
} yield {
Expand Down Expand Up @@ -762,4 +785,15 @@ object JavalibIRCleaner {
val allPairs = functionTypePairs ++ refPairs ++ tuplePairs ++ otherPairs
allPairs.toMap
}

private val ClassNameSubstitutionsForScalalib: Map[ClassName, ClassName] = {
val anonFunctionPairs = for {
n <- (0 to 22).toList
} yield {
ClassName(s"scala.scalajs.runtime.AnonFunction$n") -> ClassName(s"scala.runtime.AnonFunction$n")
}

val allPairs = anonFunctionPairs
allPairs.toMap
}
}
Loading
0