1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-09 16:29:34 +00:00

Migration from Nashorn to GraalJS

This commit is contained in:
Karol Stasiak 2020-09-22 17:59:32 +02:00
parent 1decf2f087
commit b87c40fc9c
3 changed files with 78 additions and 54 deletions

View File

@ -50,7 +50,10 @@ Test suite is useful if you plan on modifying the compiler. Some test dependenci
#### Prerequisites
* JDK 1.8 with Nashorn (tests don't work on newer versions)
* JDK 1.8 or later
* Millfork up to version 0.3.22 used to require exactly JDK 1.8 with Nashorn,
as the tests didn't work on newer versions
* sbt

View File

@ -32,6 +32,12 @@ libraryDependencies += "eu.rekawek.coffeegb" % "coffee-gb" % "1.0.0" % "test"
libraryDependencies += "roug.org.osnine" % "osnine-core" % "2.0-SNAPSHOT" % "test"
libraryDependencies += "org.graalvm.sdk" % "graal-sdk" % "20.2.0" % "test"
libraryDependencies += "org.graalvm.js" % "js" % "20.2.0" % "test"
libraryDependencies += "org.graalvm.js" % "js-scriptengine" % "20.2.0" % "test"
mainClass in Compile := Some("millfork.Main")
assemblyJarName := "millfork.jar"

View File

@ -1,27 +1,27 @@
package millfork.test.emu
import javax.script.{Bindings, ScriptEngine, ScriptEngineManager}
import java.io.FileReader
import java.nio.file.Paths
import java.nio.file.{Files, Paths}
import jdk.nashorn.api.scripting.ScriptObjectMirror
import org.graalvm.polyglot.{Context, Value}
import scala.language.dynamics
/**
* Despite its name, it no longer uses Nashorn, but GraalJS instead.
* @author Karol Stasiak
*/
object NashornEmulator {
lazy val engine: ScriptEngine = {
val jsFile = Paths.get(classOf[Nothing].getResource("/cpu.js").toURI).toFile
val engine: ScriptEngine = new ScriptEngineManager().getEngineByName("nashorn")
engine.eval(new FileReader(jsFile))
engine
lazy val engine: Context = {
val jsFile = Paths.get(classOf[Nothing].getResource("/cpu.js").toURI)
import org.graalvm.polyglot.Context
val polyglot = Context.create()
polyglot.eval("js", new String(Files.readAllBytes(jsFile)))
polyglot
}
private def newCpu(): JsObject = {
JsObject(engine.get("CPU_65816").asInstanceOf[ScriptObjectMirror]).construct()
JsObject(engine.eval("js", "CPU_65816")).construct()
}
def run(memory: Array[Byte], steps: Long, start: Int): (Long, Array[Byte]) = {
@ -32,14 +32,15 @@ object NashornEmulator {
val memory0 = cpu.mmu.memory.!("0")
for (i <- 0 until 1 << 16) memory0.!(i.toString, memory(i) & 0xff)
cpu.r.pc = start
cpu.r.sp = 0x1ff
cpu.r.k = 0
var count = 0L
while (count < steps && cpu.r.s.*[Number].intValue().&(0xff) > 1) {
while (count < steps && cpu.r.s.toInt.&(0xff) > 1) {
cpu.step()
count += 1
}
val newMemory = (0 until 1 << 16).map(i => memory0.!(i.toString).*[Number].byteValue()).toArray
val cycles = cpu.cycle_count.*[Number].longValue()
val newMemory = (0 until 1 << 16).map(i => memory0.!(i.toString).toByte).toArray
val cycles = cpu.cycle_count.toLong
cycles -> newMemory
}
@ -52,60 +53,74 @@ object NashornEmulator {
}
}
case class JsObject(private val mirror: Any) extends Dynamic {
case class JsObject(private val mirror: Value) extends Dynamic {
def !(index: Long): JsObject = {
if (mirror.hasArrayElements) return JsObject(mirror.getArrayElement(index))
else throw new IllegalArgumentException(s"Accessing index $index of $getUnderlyingClass")
}
def !(index: JsObject): JsObject =
mirror match {
case x: ScriptObjectMirror => JsObject(x.get(index.mirror))
case _ => throw new IllegalArgumentException(s"Accessing field $index of $getUnderlyingClass")
}
def !(index: String): JsObject = {
if (mirror.hasMembers) return JsObject(mirror.getMember(index))
else throw new IllegalArgumentException(s"Accessing field $index of $getUnderlyingClass")
}
def !(index: Any): JsObject =
mirror match {
case x: ScriptObjectMirror => JsObject(x.get(index))
case _ => throw new IllegalArgumentException(s"Accessing field $index of $getUnderlyingClass")
}
def !(index: String, value: JsObject): Unit = {
if (mirror.hasMembers) mirror.putMember(index, value.mirror)
else throw new IllegalArgumentException(s"Setting field $index of $getUnderlyingClass")
}
def !(index: String, value:Any): Unit =
mirror match {
case x: ScriptObjectMirror => x.setMember(index, value)
case _ => throw new IllegalArgumentException(s"Setting field $index of $getUnderlyingClass")
}
def !(index: String, value:Any): Unit = {
if (mirror.hasMembers) mirror.putMember(index, value)
else throw new IllegalArgumentException(s"Setting field $index of $getUnderlyingClass")
}
def selectDynamic(name: String): JsObject =
mirror match {
case x: ScriptObjectMirror => JsObject(x.get(name).asInstanceOf[Any] match {
case y: Double => if (y.isValidInt) y.toInt else y
case y => y
})
case _ => throw new IllegalArgumentException(s"Accessing field $name of $getUnderlyingClass")
}
def !(index: Long, value: JsObject): Unit = {
if (mirror.hasArrayElements) mirror.setArrayElement(index, value.mirror)
else throw new IllegalArgumentException(s"Setting field $index of $getUnderlyingClass")
}
def !(index: Long, value:Any): Unit = {
if (mirror.hasArrayElements) mirror.setArrayElement(index, value)
else throw new IllegalArgumentException(s"Setting field $index of $getUnderlyingClass")
}
def selectDynamic(name: String): JsObject = {
if (mirror.hasMembers) return JsObject(mirror.getMember(name))
else throw new IllegalArgumentException(s"Accessing field $name of $getUnderlyingClass")
}
private def getUnderlyingClass = {
if (mirror.asInstanceOf[AnyRef] eq null) "null" else mirror.getClass.getSimpleName
}
def updateDynamic(name: String)(value: Any): Unit =
mirror match {
case x: ScriptObjectMirror => x.setMember(name, value)
case _ => throw new IllegalArgumentException(s"Setting field $name of $getUnderlyingClass")
def updateDynamic(name: String)(value: Any): Unit = {
if (mirror.hasMembers) {
value match {
case o:JsObject =>
mirror.putMember(name, o.mirror)
case _ =>
mirror.putMember(name, value)
}
}
else throw new IllegalArgumentException(s"Setting field $name of $getUnderlyingClass")
}
def applyDynamic(name: String)(params: Any*): JsObject =
mirror match {
case x: ScriptObjectMirror => JsObject(x.callMember(name, params.toArray))
case _ => throw new IllegalArgumentException(s"Accessing field $name of $getUnderlyingClass")
}
def applyDynamic(name: String)(params: Any*): JsObject = {
if (mirror.canInvokeMember(name)) JsObject(mirror.invokeMember(name, params.toArray))
else throw new IllegalArgumentException(s"Invoking method $name of $getUnderlyingClass")
}
def construct(params: Any*): JsObject =
mirror match {
case x: ScriptObjectMirror => JsObject(x.newObject(params.toArray))
case _ => throw new IllegalArgumentException(s"Using $getUnderlyingClass as a constructor")
}
def construct(params: Any*): JsObject = {
if (mirror.canInstantiate) JsObject(mirror.newInstance(params.toArray))
else throw new IllegalArgumentException(s"Using $getUnderlyingClass as a constructor")
}
@inline
def *[T]: T = mirror.asInstanceOf[T]
def toInt: Int = mirror.asInt()
def toLong: Long = mirror.asLong()
def toByte: Byte = mirror.asInt().toByte
override def toString: String = String.valueOf(mirror)
}