Ensure LuaValues are disposed.

Adding in these missing calls prevents these instances from having to be finalized.
This commit is contained in:
RoosterDragon
2015-12-23 16:04:58 +00:00
parent 387d0d0e3f
commit 6ab6d774a7
11 changed files with 184 additions and 142 deletions

View File

@@ -64,7 +64,19 @@ namespace OpenRA.Scripting
}
}
// For global-level bindings
/// <summary>
/// Provides global bindings in Lua code.
/// </summary>
/// <remarks>
/// Instance methods and properties declared in derived classes will be made available in Lua. Use
/// <see cref="ScriptGlobalAttribute"/> on your derived class to specify the name exposed in Lua. It is recommended
/// to apply <see cref="DescAttribute"/> against each method or property to provide a description of what it does.
///
/// Any parameters to your method that are <see cref="LuaValue"/>s will be disposed automatically when your method
/// completes. If you need to return any of these values, or need them to live longer than your method, you must
/// use <see cref="LuaValue.CopyReference"/> to get your own copy of the value. Any copied values you return will
/// be disposed automatically, but you assume responsibility for disposing any other copies.
/// </remarks>
public abstract class ScriptGlobal : ScriptObjectWrapper
{
protected override string DuplicateKeyError(string memberName) { return "Table '{0}' defines multiple members '{1}'".F(Name, memberName); }
@@ -78,11 +90,27 @@ namespace OpenRA.Scripting
var type = this.GetType();
var names = type.GetCustomAttributes<ScriptGlobalAttribute>(true);
if (names.Length != 1)
throw new InvalidOperationException("[LuaGlobal] attribute not found for global table '{0}'".F(type));
throw new InvalidOperationException("[ScriptGlobal] attribute not found for global table '{0}'".F(type));
Name = names.First().Name;
Bind(new[] { this });
}
protected IEnumerable<T> FilteredObjects<T>(IEnumerable<T> objects, LuaFunction filter)
{
if (filter != null)
{
objects = objects.Where(a =>
{
using (var luaObject = a.ToLuaValue(Context))
using (var filterResult = filter.Call(luaObject))
using (var result = filterResult.First())
return result.ToBoolean();
});
}
return objects;
}
}
public sealed class ScriptGlobalAttribute : Attribute

View File

@@ -44,31 +44,54 @@ namespace OpenRA.Scripting
LuaValue Invoke(LuaVararg args)
{
if (!IsMethod)
throw new LuaException("Trying to invoke a ScriptMemberWrapper that isn't a method!");
var mi = (MethodInfo)Member;
var pi = mi.GetParameters();
var clrArgs = new object[pi.Length];
var argCount = args.Count;
for (var i = 0; i < pi.Length; i++)
object[] clrArgs = null;
try
{
if (i >= argCount)
{
if (!pi[i].IsOptional)
throw new LuaException("Argument '{0}' of '{1}' is not optional.".F(pi[i].LuaDocString(), Member.LuaDocString()));
if (!IsMethod)
throw new LuaException("Trying to invoke a ScriptMemberWrapper that isn't a method!");
clrArgs[i] = pi[i].DefaultValue;
continue;
var mi = (MethodInfo)Member;
var pi = mi.GetParameters();
clrArgs = new object[pi.Length];
var argCount = args.Count;
for (var i = 0; i < pi.Length; i++)
{
if (i >= argCount)
{
if (!pi[i].IsOptional)
throw new LuaException("Argument '{0}' of '{1}' is not optional.".F(pi[i].LuaDocString(), Member.LuaDocString()));
clrArgs[i] = pi[i].DefaultValue;
continue;
}
if (!args[i].TryGetClrValue(pi[i].ParameterType, out clrArgs[i]))
throw new LuaException("Unable to convert parameter {0} to {1}".F(i, pi[i].ParameterType.Name));
}
if (!args[i].TryGetClrValue(pi[i].ParameterType, out clrArgs[i]))
throw new LuaException("Unable to convert parameter {0} to {1}".F(i, pi[i].ParameterType.Name));
return mi.Invoke(Target, clrArgs).ToLuaValue(context);
}
finally
{
// Clean up all the Lua arguments that were given to us.
foreach (var arg in args)
arg.Dispose();
args.Dispose();
var ret = mi.Invoke(Target, clrArgs);
return ret.ToLuaValue(context);
// If we created any arrays of LuaValues to pass around, we need to dispose those too.
if (clrArgs != null)
{
foreach (var arg in clrArgs)
{
if (!(arg is LuaValue[]))
continue;
foreach (var value in (LuaValue[])arg)
value.Dispose();
}
}
}
}
public LuaValue Get(LuaRuntime runtime)
@@ -77,10 +100,7 @@ namespace OpenRA.Scripting
return runtime.CreateFunctionFromDelegate((Func<LuaVararg, LuaValue>)Invoke);
if (IsGetProperty)
{
var pi = Member as PropertyInfo;
return pi.GetValue(Target, null).ToLuaValue(context);
}
return ((PropertyInfo)Member).GetValue(Target, null).ToLuaValue(context);
throw new LuaException("The property '{0}' is write-only".F(Member.Name));
}
@@ -89,7 +109,7 @@ namespace OpenRA.Scripting
{
if (IsSetProperty)
{
var pi = Member as PropertyInfo;
var pi = (PropertyInfo)Member;
object clrValue;
if (!value.TryGetClrValue(pi.PropertyType, out clrValue))
throw new LuaException("Unable to convert '{0}' to Clr type '{1}'".F(value.WrappedClrType().Name, pi.PropertyType));

View File

@@ -107,13 +107,22 @@ namespace OpenRA.Scripting
foreach (var kv in table)
{
object element;
if (innerType == typeof(LuaValue))
element = kv.Value;
else if (!kv.Value.TryGetClrValue(innerType, out element))
throw new LuaException("Unable to convert table value of type {0} to type {1}".F(kv.Value.WrappedClrType(), innerType));
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++);
array.SetValue(element, i++);
}
}
clrObject = array;
@@ -159,11 +168,13 @@ namespace OpenRA.Scripting
if (obj is Array)
{
var array = obj as Array;
var array = (Array)obj;
var i = 1;
var table = context.CreateTable();
foreach (var x in array)
table.Add(i++, x.ToLuaValue(context));
using (LuaValue key = i++, value = x.ToLuaValue(context))
table.Add(key, value);
return table;
}