diff --git a/build.mill b/build.mill index 015591e81..628ad9a44 100644 --- a/build.mill +++ b/build.mill @@ -402,7 +402,12 @@ object anthology extends SoundnessModule { object `scala` extends SoundnessSubModule { def moduleDeps = Seq(core, anticipation.log, ambience.core, galilei.core) - def compileIvyDeps = Agg(ivy"org.scala-lang:scala3-compiler_3:${scalaVersion()}") + def ivyDeps = Agg( + ivy"org.scala-lang:scala3-compiler_3:${scalaVersion()}", + ivy"org.scala-js:scalajs-library_2.13:1.18.2", + ivy"org.scala-js:scalajs-javalib:1.18.2", + ivy"org.scala-js:scalajs-linker_2.13:1.18.2" + ) } object `java` extends SoundnessSubModule { diff --git a/lib/anthology/src/scala/anthology-scala.scala b/lib/anthology/src/scala/anthology-scala.scala index 1b3f6eaf1..f5e354eb6 100644 --- a/lib/anthology/src/scala/anthology-scala.scala +++ b/lib/anthology/src/scala/anthology-scala.scala @@ -44,10 +44,11 @@ import dotty.tools.dotc.reporting.* import language.adhocExtensions -object scalacOptions: - val newSyntax = Scalac.Option[Scalac.Versions](t"-new-syntax") - def sourceFuture = Scalac.Option[Scalac.Versions](t"-source", t"future") - val experimental = Scalac.Option[3.4 | 3.5 | 3.6 | 3.7](t"-experimental") +package scalacOptions: + val newSyntax = ScalacOption[Scalac.All](t"-new-syntax") + def sourceFuture = ScalacOption[Scalac.All](t"-source", t"future") + val experimental = ScalacOption[3.4 | 3.5 | 3.6 | 3.7](t"-experimental") + val scalaJs = ScalacOption[Scalac.All](t"-scalajs") object warnings: val feature = Scalac.Option[Scalac.Versions](t"-feature") diff --git a/lib/anthology/src/scala/anthology.Scalac.scala b/lib/anthology/src/scala/anthology.Scalac.scala index 1d1070643..8eca780a4 100644 --- a/lib/anthology/src/scala/anthology.Scalac.scala +++ b/lib/anthology/src/scala/anthology.Scalac.scala @@ -62,8 +62,7 @@ object Scalac: def refresh(): Unit = synchronized { Scala3 = new dtd.Compiler() } def compiler(): dtd.Compiler = Scala3 -case class Scalac[version <: Scalac.Versions](options: List[Scalac.Option[version]]): - +case class Scalac[version <: Scalac.All](options: List[Scalac.Option[version]]): def commandLineArguments: List[Text] = options.flatMap(_.flags) def apply(classpath: LocalClasspath)[path: Abstractable across Paths to Text] @@ -82,15 +81,17 @@ case class Scalac[version <: Scalac.Versions](options: List[Scalac.Option[versio object ProgressApi extends dtdsi.ProgressCallback: private var last: Int = -1 + override def informUnitStarting(stage: String | Null, unit: dtd.CompilationUnit | Null) : Unit = - () + + () override def progress (current: Int, - total: Int, - currentStage: String | Null, - nextStage: String | Null) + total: Int, + currentStage: String | Null, + nextStage: String | Null) : Boolean = val int = (100.0*current/total).toInt @@ -114,9 +115,12 @@ case class Scalac[version <: Scalac.Versions](options: List[Scalac.Option[versio ::: List(t"") Log.info(CompileEvent.Running(args)) - setup(args.map(_.s).to(Array), ctx).map(_(1)).get + + setup(args.map(_.s).to(Array), ctx).map(_(1)).getOrElse: + abort(CompilerError()) def run(): CompileProcess = + println("running") given dtdc.Contexts.Context = currentCtx.fresh.pipe: ctx => ctx . setReporter(reporter) @@ -129,13 +133,60 @@ case class Scalac[version <: Scalac.Versions](options: List[Scalac.Option[versio scalacProcess.put: task(t"scalac"): try + println("compiling") Scalac.compiler().newRun.tap: run => run.compileSources(sourceFiles) + println("and") if !reporter.hasErrors then finish(Scalac.Scala3, run) scalacProcess.put (if reporter.hasErrors then CompileResult.Failure else CompileResult.Success) + println(options) + if options.has(scalacOptions.scalaJs) then + println("has Scala.JS") + import org.scalajs.linker.* + import org.scalajs.linker.interface.* + import org.scalajs.logging.* + import scala.concurrent.*, duration.* + + val logger = new Logger: + def log(level: Level, message: => String): Unit = println(message) + def trace(exception: => Throwable): Unit = exception.printStackTrace() + + println("logger = "+logger) + + val config = StandardConfig().withExperimentalUseWebAssembly(true).withOptimizer(true).withMinify(true).withModuleKind(ModuleKind.ESModule) + println("config = "+config) + + val linker = try StandardImpl.linker(config) + catch case error: Throwable => + error.printStackTrace() + ??? + + println("linker = "+linker) + val irFileCache: IRFileCache = StandardImpl.irFileCache() + val cache = irFileCache.newCache + import scala.concurrent.ExecutionContext.Implicits.global + val scalalib: Seq[IRContainer] = Await.result(PathIRContainer.fromClasspath(List(java.nio.file.Paths.get("/Users/propensive/work/soundness/scalajs-library_2.13-1.18.2.jar").nn)), 5.minutes)(0) + val scalalib2: Seq[IRContainer] = Await.result(PathIRContainer.fromClasspath(List(java.nio.file.Paths.get("/Users/propensive/work/soundness/scalajs-scalalib_2.13-2.13.16+1.18.2.jar").nn)), 5.minutes)(0) + val javalib: Seq[IRContainer] = Await.result(PathIRContainer.fromClasspath(List(java.nio.file.Paths.get("/Users/propensive/work/soundness/scalajs-javalib-1.18.2.jar").nn)), 5.minutes)(0) + val scalalibFiles: Seq[IRFile] = Await.result(cache.cached(scalalib), 5.minutes) + val scalalibFiles2: Seq[IRFile] = Await.result(cache.cached(scalalib2), 5.minutes) + val javalibFiles: Seq[IRFile] = Await.result(cache.cached(javalib), 5.minutes) + + println("linking") + val output = PathOutputDirectory(java.nio.file.Paths.get("js").nn) + println(out.generic.s+"/foo/Hello$.sjsir") + val irFile1: IRFile = Await.result(PathIRFile(java.nio.file.Paths.get(out.generic.s+"/foo/Hello$.sjsir").nn), 5.minutes) + val irFile2: IRFile = Await.result(PathIRFile(java.nio.file.Paths.get(out.generic.s+"/foo/Hello.sjsir").nn), 5.minutes) + val mainMethod = ModuleInitializer.mainMethod("foo.Hello", "mainxyz") + println(mainMethod) + try println("Result: "+Await.result(linker.link(List(irFile1, irFile2) ++ scalalibFiles ++ scalalibFiles2 ++ javalibFiles, mainMethod :: Nil, output, logger), 1.minutes)) + catch case throwable: Throwable => + println(throwable) + throwable.printStackTrace() + catch case suc.NonFatal(error) => scalacProcess.put(CompileResult.Crash(error.stackTrace)) Scalac.refresh() diff --git a/lib/anthology/src/test/anthology.Tests.scala b/lib/anthology/src/test/anthology.Tests.scala index 142ea4569..587cfa6d2 100644 --- a/lib/anthology/src/test/anthology.Tests.scala +++ b/lib/anthology/src/test/anthology.Tests.scala @@ -34,6 +34,86 @@ package anthology import soundness.* +import workingDirectories.jre +import systemProperties.jre +import supervisors.global +import asyncTermination.cancel +import logging.silent +import stdioSources.virtualMachine.ansi + +import strategies.throwUnsafely + object Tests extends Suite(m"Anthology Tests"): def run(): Unit = - () + + suite(m"Scala compiler tests"): + + val helloWorld = t""" + package foo + + @main + def run(): Unit = println("Hello world") + """ + + val sources = Map(t"hello.scala" -> helloWorld) + + test(m"Compile hello world"): + val classpath = LocalClasspath.system() + val dir: Path on Linux = workingDirectory[Path on Linux] / Name[Linux](t"tmp") + + recover: + case CompilerError() => + println(t"Compiler error") + + case AsyncError(_) => + println(t"Async error") + + . within: + val process = Scalac[3.6](Nil)(classpath)(sources, dir) + val progress = async(process.progress.each(println(_))) + + process.complete() + process.notices.each(println(_)) + progress.await() + + . assert() + + suite(m"Scala.js compiler tests"): + + val helloWorld = t""" + package foo + + object Hello: + def mainxyz(): Unit = + def recur(n: Int, m: Int, c: Int): Int = + if c == 0 then n else recur(m, n + m, c - 1) + + val result = recur(1, 2, 8) + println(result) + + """ + + + val sources = Map(t"hello.scala" -> helloWorld) + + test(m"Compile hello world"): + val classpath = LocalClasspath.system() + val dir: Path on Linux = workingDirectory[Path on Linux] / Name[Linux](t"tmp") + + recover: + case CompilerError() => + println(t"Compiler error") + + case AsyncError(_) => + println(t"Async error") + + . within: + val process = Scalac[3.6](List(scalacOptions.scalaJs))(classpath)(sources, dir) + val progress = async(process.progress.each(println(_))) + + process.complete() + process.notices.each(println(_)) + progress.await() + + + . assert() diff --git a/lib/capricious/src/core/capricious.Random.scala b/lib/capricious/src/core/capricious.Random.scala index 7fc30e2d4..16e5ceb2a 100644 --- a/lib/capricious/src/core/capricious.Random.scala +++ b/lib/capricious/src/core/capricious.Random.scala @@ -48,5 +48,4 @@ class Random(private val generator: su.Random): def unitInterval(): Double = generator.nextDouble() def apply[value: Randomizable](): value = value.from(this) - transparent inline def shuffle[element](seq: Seq[element]): Seq[element] = - generator.shuffle(seq) + transparent inline def shuffle[element](seq: Seq[element]): Seq[element] = generator.shuffle(seq) diff --git a/lib/digression/src/core/digression.StackTrace.scala b/lib/digression/src/core/digression.StackTrace.scala index fc87f722f..3c61d105f 100644 --- a/lib/digression/src/core/digression.StackTrace.scala +++ b/lib/digression/src/core/digression.StackTrace.scala @@ -61,7 +61,7 @@ object StackTrace: "ⲛ".tt -> "class initializer".tt, "ℓ".tt -> "lazy initializer".tt, "Σ".tt -> "specialized method".tt, - "Ξ".tt -> "object".tt) + "✶".tt -> "object".tt) def rewrite(name: String, method: Boolean = false): Text = val buffer: StringBuilder = StringBuilder() @@ -228,7 +228,7 @@ object StackTrace: else if rewritten.s.endsWith("#") then val pivot = rewritten.s.lastIndexOf(".") - val sub = if rewritten.s.endsWith("⋮#") then "⋮" else "Ξ" + val sub = if rewritten.s.endsWith("⋮#") then "⋮" else "✶" (rewritten.s.substring(0, pivot).nn+"."+sub+rewritten.s.substring(pivot + 1).nn.dropRight(1)) . tt diff --git a/lib/hellenism/src/core/hellenism.Classpath.scala b/lib/hellenism/src/core/hellenism.Classpath.scala index 56004ee65..f84c506cb 100644 --- a/lib/hellenism/src/core/hellenism.Classpath.scala +++ b/lib/hellenism/src/core/hellenism.Classpath.scala @@ -34,6 +34,7 @@ package hellenism import java.net as jn +import ambience.* import anticipation.* import contingency.* import fulminate.* diff --git a/lib/hellenism/src/core/hellenism.LocalClasspath.scala b/lib/hellenism/src/core/hellenism.LocalClasspath.scala index 6ed3cfad3..473c00a1b 100644 --- a/lib/hellenism/src/core/hellenism.LocalClasspath.scala +++ b/lib/hellenism/src/core/hellenism.LocalClasspath.scala @@ -32,6 +32,8 @@ */ package hellenism +import java.lang as jl + import ambience.* import anticipation.* import contingency.* @@ -62,6 +64,8 @@ object LocalClasspath: new LocalClasspath(entries, entries.to(Set)) + def system()(using SystemProperties): LocalClasspath raises SystemPropertyError = + jl.System.getProperty("java.class.path").nn.tt.decode[LocalClasspath] def apply (entries: List