| 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) |
| } |
| |
| } |