193 lines
4.5 KiB
C#
193 lines
4.5 KiB
C#
#region Copyright & License Information
|
|
/*
|
|
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
|
* This file is part of OpenRA, which is free software. It is made
|
|
* available to you under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation, either version 3 of
|
|
* the License, or (at your option) any later version. For more
|
|
* information, see COPYING.
|
|
*/
|
|
#endregion
|
|
|
|
using System;
|
|
using Eluant;
|
|
|
|
namespace OpenRA.Scripting
|
|
{
|
|
public static class LuaValueExts
|
|
{
|
|
public static Type WrappedClrType(this LuaValue value)
|
|
{
|
|
if (value.TryGetClrObject(out var inner))
|
|
return inner.GetType();
|
|
|
|
return value.GetType();
|
|
}
|
|
|
|
public static bool TryGetClrValue<T>(this LuaValue value, out T clrObject)
|
|
{
|
|
var ret = value.TryGetClrValue(typeof(T), out object temp);
|
|
clrObject = ret ? (T)temp : default(T);
|
|
return ret;
|
|
}
|
|
|
|
public static bool TryGetClrValue(this LuaValue value, Type t, out object clrObject)
|
|
{
|
|
// Is t a nullable?
|
|
// If yes, get the underlying type
|
|
var nullable = Nullable.GetUnderlyingType(t);
|
|
if (nullable != null)
|
|
t = nullable;
|
|
|
|
// Value wraps a CLR object
|
|
if (value.TryGetClrObject(out var temp))
|
|
{
|
|
if (temp.GetType() == t)
|
|
{
|
|
clrObject = temp;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (value is LuaNil && !t.IsValueType)
|
|
{
|
|
clrObject = null;
|
|
return true;
|
|
}
|
|
|
|
if (value is LuaBoolean && t.IsAssignableFrom(typeof(bool)))
|
|
{
|
|
clrObject = value.ToBoolean();
|
|
return true;
|
|
}
|
|
|
|
if (value is LuaNumber && t.IsAssignableFrom(typeof(double)))
|
|
{
|
|
clrObject = value.ToNumber().Value;
|
|
return true;
|
|
}
|
|
|
|
// Need an explicit test for double -> int
|
|
// TODO: Lua 5.3 will introduce an integer type, so this will be able to go away
|
|
if (value is LuaNumber && t.IsAssignableFrom(typeof(int)))
|
|
{
|
|
clrObject = (int)value.ToNumber().Value;
|
|
return true;
|
|
}
|
|
|
|
if (value is LuaNumber && t.IsAssignableFrom(typeof(short)))
|
|
{
|
|
clrObject = (short)value.ToNumber().Value;
|
|
return true;
|
|
}
|
|
|
|
if (value is LuaNumber && t.IsAssignableFrom(typeof(byte)))
|
|
{
|
|
clrObject = (byte)value.ToNumber().Value;
|
|
return true;
|
|
}
|
|
|
|
if (value is LuaString && t.IsAssignableFrom(typeof(string)))
|
|
{
|
|
clrObject = value.ToString();
|
|
return true;
|
|
}
|
|
|
|
if (value is LuaFunction && t.IsAssignableFrom(typeof(LuaFunction)))
|
|
{
|
|
clrObject = value;
|
|
return true;
|
|
}
|
|
|
|
if (value is LuaTable && t.IsAssignableFrom(typeof(LuaTable)))
|
|
{
|
|
clrObject = value;
|
|
return true;
|
|
}
|
|
|
|
// Translate LuaTable<int, object> -> object[]
|
|
if (value is LuaTable && t.IsArray)
|
|
{
|
|
var innerType = t.GetElementType();
|
|
var table = (LuaTable)value;
|
|
var array = Array.CreateInstance(innerType, table.Count);
|
|
var i = 0;
|
|
|
|
foreach (var kv in table)
|
|
{
|
|
using (kv.Key)
|
|
{
|
|
object element;
|
|
if (innerType == typeof(LuaValue))
|
|
element = kv.Value;
|
|
else
|
|
{
|
|
var elementHasClrValue = kv.Value.TryGetClrValue(innerType, out element);
|
|
if (!elementHasClrValue || !(element is LuaValue))
|
|
kv.Value.Dispose();
|
|
if (!elementHasClrValue)
|
|
throw new LuaException("Unable to convert table value of type {0} to type {1}".F(kv.Value.WrappedClrType(), innerType));
|
|
}
|
|
|
|
array.SetValue(element, i++);
|
|
}
|
|
}
|
|
|
|
clrObject = array;
|
|
return true;
|
|
}
|
|
|
|
// Value isn't of the requested type.
|
|
// Set a default output value and return false
|
|
// Value types are assumed to specify a default constructor
|
|
clrObject = t.IsValueType ? Activator.CreateInstance(t) : null;
|
|
return false;
|
|
}
|
|
|
|
public static LuaValue ToLuaValue(this object obj, ScriptContext context)
|
|
{
|
|
if (obj is LuaValue)
|
|
return (LuaValue)obj;
|
|
|
|
if (obj == null)
|
|
return LuaNil.Instance;
|
|
|
|
if (obj is double)
|
|
return (double)obj;
|
|
|
|
if (obj is int)
|
|
return (int)obj;
|
|
|
|
if (obj is bool)
|
|
return (bool)obj;
|
|
|
|
if (obj is string)
|
|
return (string)obj;
|
|
|
|
if (obj is IScriptBindable)
|
|
{
|
|
// Object needs additional notification / context
|
|
var notify = obj as IScriptNotifyBind;
|
|
notify?.OnScriptBind(context);
|
|
|
|
return new LuaCustomClrObject(obj);
|
|
}
|
|
|
|
if (obj is Array)
|
|
{
|
|
var array = (Array)obj;
|
|
var i = 1;
|
|
var table = context.CreateTable();
|
|
|
|
foreach (var x in array)
|
|
using (LuaValue key = i++, value = x.ToLuaValue(context))
|
|
table.Add(key, value);
|
|
|
|
return table;
|
|
}
|
|
|
|
throw new InvalidOperationException("Cannot convert type '{0}' to Lua. Class must implement IScriptBindable.".F(obj.GetType()));
|
|
}
|
|
}
|
|
}
|