mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-16 00:31:30 +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
|
||||
|
||||
* 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
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user