blob: 9e99f18491ad7052e58fcc8c8b70048b3ca63874 [file] [log] [blame] [raw]
package li.cil.oc.util
import java.util
import li.cil.oc.OpenComputers
import li.cil.oc.Settings
import li.cil.oc.api.machine.Value
import li.cil.repack.com.naef.jnlua.JavaFunction
import li.cil.repack.com.naef.jnlua.LuaState
import li.cil.repack.com.naef.jnlua.LuaType
import scala.collection.convert.WrapAsScala._
import scala.collection.mutable
import scala.language.implicitConversions
import scala.math.ScalaNumber
import scala.runtime.BoxedUnit
object ExtendedLuaState {
implicit def extendLuaState(state: LuaState): ExtendedLuaState = new ExtendedLuaState(state)
class ExtendedLuaState(val lua: LuaState) {
def pushScalaFunction(f: (LuaState) => Int) = lua.pushJavaFunction(new JavaFunction {
override def invoke(state: LuaState) = f(state)
})
def pushValue(value: Any, memo: util.IdentityHashMap[Any, Int] = new util.IdentityHashMap()) {
val recursive = memo.size > 0
val oldTop = lua.getTop
if (memo.containsKey(value)) {
lua.pushValue(memo.get(value))
}
else {
(value match {
case number: ScalaNumber => number.underlying
case reference: AnyRef => reference
case null => null
case primitive => primitive.asInstanceOf[AnyRef]
}) match {
case null | Unit | _: BoxedUnit => lua.pushNil()
case value: java.lang.Boolean => lua.pushBoolean(value.booleanValue)
case value: java.lang.Byte => lua.pushNumber(value.byteValue)
case value: java.lang.Character => lua.pushString(String.valueOf(value))
case value: java.lang.Short => lua.pushNumber(value.shortValue)
case value: java.lang.Integer => lua.pushNumber(value.intValue)
case value: java.lang.Long => lua.pushNumber(value.longValue)
case value: java.lang.Float => lua.pushNumber(value.floatValue)
case value: java.lang.Double => lua.pushNumber(value.doubleValue)
case value: java.lang.String => lua.pushString(value)
case value: Array[Byte] => lua.pushByteArray(value)
case value: Array[_] => pushList(value, value.zipWithIndex.iterator, memo)
case value: Value if Settings.get.allowUserdata => lua.pushJavaObjectRaw(value)
case value: Product => pushList(value, value.productIterator.zipWithIndex, memo)
case value: Seq[_] => pushList(value, value.zipWithIndex.iterator, memo)
case value: java.util.Map[_, _] => pushTable(value, value.toMap, memo)
case value: Map[_, _] => pushTable(value, value, memo)
case value: mutable.Map[_, _] => pushTable(value, value.toMap, memo)
case _ =>
OpenComputers.log.warn("Tried to push an unsupported value of type to Lua: " + value.getClass.getName + ".")
lua.pushNil()
}
// Remove values kept on the stack for memoization if this is the
// original call (not a recursive one, where we might need the memo
// info even after returning).
if (!recursive) {
lua.setTop(oldTop + 1)
}
}
}
def pushList(obj: AnyRef, list: Iterator[(Any, Int)], memo: util.IdentityHashMap[Any, Int]) {
lua.newTable()
val tableIndex = lua.getTop
memo += obj -> tableIndex
var count = 0
list.foreach {
case (value, index) =>
pushValue(value, memo)
lua.rawSet(tableIndex, index + 1)
count = count + 1
}
// Bring table back to top (in case memo values were pushed).
lua.pushValue(tableIndex)
lua.pushString("n")
lua.pushInteger(count)
lua.rawSet(-3)
}
def pushTable(obj: AnyRef, map: Map[_, _], memo: util.IdentityHashMap[Any, Int]) {
lua.newTable(0, map.size)
val tableIndex = lua.getTop
memo += obj -> tableIndex
for ((key: AnyRef, value: AnyRef) <- map) {
if (key != null && key != Unit && !key.isInstanceOf[BoxedUnit]) {
pushValue(key, memo)
val keyIndex = lua.getTop
pushValue(value, memo)
// Bring key to front, in case of memo from value push.
// Cannot actually move because that might shift memo info.
lua.pushValue(keyIndex)
lua.insert(-2)
lua.setTable(tableIndex)
}
}
// Bring table back to top (in case memo values were pushed).
lua.pushValue(tableIndex)
}
def toSimpleJavaObject(index: Int): AnyRef = lua.`type`(index) match {
case LuaType.BOOLEAN => Boolean.box(lua.toBoolean(index))
case LuaType.NUMBER => Double.box(lua.toNumber(index))
case LuaType.STRING => lua.toByteArray(index)
case LuaType.TABLE => lua.toJavaObject(index, classOf[java.util.Map[_, _]])
case LuaType.USERDATA => lua.toJavaObjectRaw(index)
case _ => null
}
def toSimpleJavaObjects(start: Int) =
for (index <- start to lua.getTop) yield toSimpleJavaObject(index)
}
}