forked from OpenRA/OpenRA
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathScriptMemberWrapper.cs
157 lines (133 loc) · 4.34 KB
/
ScriptMemberWrapper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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 System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Eluant;
using OpenRA.Traits;
namespace OpenRA.Scripting
{
public class ScriptMemberWrapper
{
readonly ScriptContext context;
public readonly object Target;
public readonly MemberInfo Member;
public readonly bool IsMethod;
public readonly bool IsGetProperty;
public readonly bool IsSetProperty;
public ScriptMemberWrapper(ScriptContext context, object target, MemberInfo mi)
{
this.context = context;
Target = target;
Member = mi;
var property = mi as PropertyInfo;
if (property != null)
{
IsGetProperty = property.GetGetMethod() != null;
IsSetProperty = property.GetSetMethod() != null;
}
else
IsMethod = true;
}
LuaValue Invoke(LuaVararg args)
{
object[] clrArgs = null;
try
{
if (!IsMethod)
throw new LuaException("Trying to invoke a ScriptMemberWrapper that isn't a method!");
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 '{pi[i].LuaDocString()}' of '{Member.LuaDocString()}' is not optional.");
clrArgs[i] = pi[i].DefaultValue;
continue;
}
if (!args[i].TryGetClrValue(pi[i].ParameterType, out clrArgs[i]))
throw new LuaException($"Unable to convert parameter {i} to {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();
// 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 not LuaValue[] table)
continue;
foreach (var value in table)
value.Dispose();
}
}
}
}
public LuaValue Get(LuaRuntime runtime)
{
if (IsMethod)
return runtime.CreateFunctionFromDelegate((Func<LuaVararg, LuaValue>)Invoke);
if (IsGetProperty)
return ((PropertyInfo)Member).GetValue(Target, null).ToLuaValue(context);
throw new LuaException($"The property '{Member.Name}' is write-only");
}
public void Set(LuaValue value)
{
if (IsSetProperty)
{
var pi = (PropertyInfo)Member;
if (!value.TryGetClrValue(pi.PropertyType, out var clrValue))
throw new LuaException($"Unable to convert '{value.WrappedClrType().Name}' to CLR type '{pi.PropertyType}'");
pi.SetValue(Target, clrValue, null);
}
else
throw new LuaException($"The property '{Member.Name}' is read-only");
}
public static IEnumerable<MemberInfo> WrappableMembers(Type t)
{
// Only expose defined public non-static methods that were explicitly declared by the author
var flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
foreach (var mi in t.GetMembers(flags))
{
// Properties are always wrappable
if (mi is PropertyInfo)
yield return mi;
// Methods are allowed if they aren't generic, and aren't generated by the compiler
var method = mi as MethodInfo;
if (method != null && !method.IsGenericMethodDefinition && !method.IsSpecialName)
yield return mi;
// Fields aren't allowed
}
}
public static string[] RequiredTraitNames(Type t)
{
// Returns the inner types of all the Requires<T> interfaces on this type
var types = t.GetInterfaces()
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(Requires<>));
// Remove the namespace and the trailing "Info"
return types.SelectMany(i => i.GetGenericArguments())
.Select(g => g.Name.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault())
.Select(s => s.EndsWith("Info") ? s.Remove(s.Length - 4, 4) : s)
.ToArray();
}
}
}