mirror of
https://github.com/KarolS/millfork.git
synced 2024-10-20 03:24:04 +00:00
Migration from Nashorn to GraalJS
This commit is contained in:
parent
1decf2f087
commit
b87c40fc9c
@ -50,7 +50,10 @@ Test suite is useful if you plan on modifying the compiler. Some test dependenci
|
|||||||
|
|
||||||
#### Prerequisites
|
#### 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
|
* sbt
|
||||||
|
|
||||||
|
@ -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 += "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")
|
mainClass in Compile := Some("millfork.Main")
|
||||||
|
|
||||||
assemblyJarName := "millfork.jar"
|
assemblyJarName := "millfork.jar"
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
package millfork.test.emu
|
package millfork.test.emu
|
||||||
|
|
||||||
import javax.script.{Bindings, ScriptEngine, ScriptEngineManager}
|
import java.nio.file.{Files, Paths}
|
||||||
import java.io.FileReader
|
|
||||||
import java.nio.file.Paths
|
|
||||||
|
|
||||||
import jdk.nashorn.api.scripting.ScriptObjectMirror
|
import org.graalvm.polyglot.{Context, Value}
|
||||||
|
|
||||||
import scala.language.dynamics
|
import scala.language.dynamics
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Despite its name, it no longer uses Nashorn, but GraalJS instead.
|
||||||
* @author Karol Stasiak
|
* @author Karol Stasiak
|
||||||
*/
|
*/
|
||||||
object NashornEmulator {
|
object NashornEmulator {
|
||||||
|
|
||||||
lazy val engine: ScriptEngine = {
|
lazy val engine: Context = {
|
||||||
val jsFile = Paths.get(classOf[Nothing].getResource("/cpu.js").toURI).toFile
|
val jsFile = Paths.get(classOf[Nothing].getResource("/cpu.js").toURI)
|
||||||
val engine: ScriptEngine = new ScriptEngineManager().getEngineByName("nashorn")
|
import org.graalvm.polyglot.Context
|
||||||
engine.eval(new FileReader(jsFile))
|
val polyglot = Context.create()
|
||||||
engine
|
polyglot.eval("js", new String(Files.readAllBytes(jsFile)))
|
||||||
|
polyglot
|
||||||
}
|
}
|
||||||
|
|
||||||
private def newCpu(): JsObject = {
|
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]) = {
|
def run(memory: Array[Byte], steps: Long, start: Int): (Long, Array[Byte]) = {
|
||||||
@ -32,14 +32,15 @@ object NashornEmulator {
|
|||||||
val memory0 = cpu.mmu.memory.!("0")
|
val memory0 = cpu.mmu.memory.!("0")
|
||||||
for (i <- 0 until 1 << 16) memory0.!(i.toString, memory(i) & 0xff)
|
for (i <- 0 until 1 << 16) memory0.!(i.toString, memory(i) & 0xff)
|
||||||
cpu.r.pc = start
|
cpu.r.pc = start
|
||||||
|
cpu.r.sp = 0x1ff
|
||||||
cpu.r.k = 0
|
cpu.r.k = 0
|
||||||
var count = 0L
|
var count = 0L
|
||||||
while (count < steps && cpu.r.s.*[Number].intValue().&(0xff) > 1) {
|
while (count < steps && cpu.r.s.toInt.&(0xff) > 1) {
|
||||||
cpu.step()
|
cpu.step()
|
||||||
count += 1
|
count += 1
|
||||||
}
|
}
|
||||||
val newMemory = (0 until 1 << 16).map(i => memory0.!(i.toString).*[Number].byteValue()).toArray
|
val newMemory = (0 until 1 << 16).map(i => memory0.!(i.toString).toByte).toArray
|
||||||
val cycles = cpu.cycle_count.*[Number].longValue()
|
val cycles = cpu.cycle_count.toLong
|
||||||
cycles -> newMemory
|
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 =
|
def !(index: String): JsObject = {
|
||||||
mirror match {
|
if (mirror.hasMembers) return JsObject(mirror.getMember(index))
|
||||||
case x: ScriptObjectMirror => JsObject(x.get(index.mirror))
|
else throw new IllegalArgumentException(s"Accessing field $index of $getUnderlyingClass")
|
||||||
case _ => throw new IllegalArgumentException(s"Accessing field $index of $getUnderlyingClass")
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def !(index: Any): JsObject =
|
def !(index: String, value: JsObject): Unit = {
|
||||||
mirror match {
|
if (mirror.hasMembers) mirror.putMember(index, value.mirror)
|
||||||
case x: ScriptObjectMirror => JsObject(x.get(index))
|
else throw new IllegalArgumentException(s"Setting field $index of $getUnderlyingClass")
|
||||||
case _ => throw new IllegalArgumentException(s"Accessing field $index of $getUnderlyingClass")
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def !(index: String, value:Any): Unit =
|
def !(index: String, value:Any): Unit = {
|
||||||
mirror match {
|
if (mirror.hasMembers) mirror.putMember(index, value)
|
||||||
case x: ScriptObjectMirror => x.setMember(index, value)
|
else throw new IllegalArgumentException(s"Setting field $index of $getUnderlyingClass")
|
||||||
case _ => throw new IllegalArgumentException(s"Setting field $index of $getUnderlyingClass")
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def selectDynamic(name: String): JsObject =
|
def !(index: Long, value: JsObject): Unit = {
|
||||||
mirror match {
|
if (mirror.hasArrayElements) mirror.setArrayElement(index, value.mirror)
|
||||||
case x: ScriptObjectMirror => JsObject(x.get(name).asInstanceOf[Any] match {
|
else throw new IllegalArgumentException(s"Setting field $index of $getUnderlyingClass")
|
||||||
case y: Double => if (y.isValidInt) y.toInt else y
|
}
|
||||||
case y => y
|
|
||||||
})
|
def !(index: Long, value:Any): Unit = {
|
||||||
case _ => throw new IllegalArgumentException(s"Accessing field $name of $getUnderlyingClass")
|
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 = {
|
private def getUnderlyingClass = {
|
||||||
if (mirror.asInstanceOf[AnyRef] eq null) "null" else mirror.getClass.getSimpleName
|
if (mirror.asInstanceOf[AnyRef] eq null) "null" else mirror.getClass.getSimpleName
|
||||||
}
|
}
|
||||||
|
|
||||||
def updateDynamic(name: String)(value: Any): Unit =
|
def updateDynamic(name: String)(value: Any): Unit = {
|
||||||
mirror match {
|
if (mirror.hasMembers) {
|
||||||
case x: ScriptObjectMirror => x.setMember(name, value)
|
value match {
|
||||||
case _ => throw new IllegalArgumentException(s"Setting field $name of $getUnderlyingClass")
|
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 =
|
def applyDynamic(name: String)(params: Any*): JsObject = {
|
||||||
mirror match {
|
if (mirror.canInvokeMember(name)) JsObject(mirror.invokeMember(name, params.toArray))
|
||||||
case x: ScriptObjectMirror => JsObject(x.callMember(name, params.toArray))
|
else throw new IllegalArgumentException(s"Invoking method $name of $getUnderlyingClass")
|
||||||
case _ => throw new IllegalArgumentException(s"Accessing field $name of $getUnderlyingClass")
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def construct(params: Any*): JsObject =
|
def construct(params: Any*): JsObject = {
|
||||||
mirror match {
|
if (mirror.canInstantiate) JsObject(mirror.newInstance(params.toArray))
|
||||||
case x: ScriptObjectMirror => JsObject(x.newObject(params.toArray))
|
else throw new IllegalArgumentException(s"Using $getUnderlyingClass as a constructor")
|
||||||
case _ => throw new IllegalArgumentException(s"Using $getUnderlyingClass as a constructor")
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@inline
|
def toInt: Int = mirror.asInt()
|
||||||
def *[T]: T = mirror.asInstanceOf[T]
|
|
||||||
|
def toLong: Long = mirror.asLong()
|
||||||
|
|
||||||
|
def toByte: Byte = mirror.asInt().toByte
|
||||||
|
|
||||||
override def toString: String = String.valueOf(mirror)
|
override def toString: String = String.valueOf(mirror)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user