Skip to content

Commit 97730ea

Browse files
Add variant of MappedClassField that generates getters and setters at runtime
1 parent aa4364d commit 97730ea

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

UnityMapper/API/Properties.cs

+75
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Reflection;
2+
using System.Reflection.Emit;
23
using UnityEngine;
34

45
namespace UnityMapper;
@@ -82,4 +83,78 @@ public string GetName()
8283
{
8384
return info.DeclaringType.Name + "/" + info.Name;
8485
}
86+
}
87+
88+
/// <summary>
89+
/// Implementation of IMappedProperty that uses runtime code generation to improve performance.
90+
/// </summary>
91+
public class RCGClassField : IMappedProperty
92+
{
93+
94+
private delegate void SetObjectDelegate(object target, object value);
95+
private delegate object GetValueDelegate(object target);
96+
97+
private object _target;
98+
private FieldInfo _info;
99+
private SetObjectDelegate _setter;
100+
private GetValueDelegate _getter;
101+
102+
public RCGClassField(FieldInfo info, Component target)
103+
{
104+
_target = target;
105+
_info = info;
106+
_setter = CreateSetter();
107+
_getter = CreateGetter();
108+
}
109+
110+
public Type GetMappedType()
111+
{
112+
return _info.FieldType;
113+
}
114+
115+
public void SetObject(object value)
116+
{
117+
_setter(_target, value);
118+
}
119+
120+
public object GetValue()
121+
{
122+
return _getter(_target);
123+
}
124+
125+
public string GetName()
126+
{
127+
return _info.DeclaringType.Name + "/" + _info.Name;
128+
}
129+
130+
private GetValueDelegate CreateGetter()
131+
{
132+
var d = new DynamicMethod($"Get{_info.Name}_Generated", typeof(object), new[] {typeof(object)}, _info.DeclaringType);
133+
var il = d.GetILGenerator();
134+
il.Emit(OpCodes.Ldarg_0); // "this"
135+
il.Emit(OpCodes.Castclass, _info.DeclaringType); // cast to the declaring type
136+
il.Emit(OpCodes.Ldfld, _info); // "this".{_info}
137+
if (_info.FieldType.IsValueType) // box it if it's a value type
138+
{
139+
il.Emit(OpCodes.Box, _info.FieldType);
140+
}
141+
il.Emit(OpCodes.Ret); // return the value
142+
return (GetValueDelegate) d.CreateDelegate(typeof(GetValueDelegate));
143+
}
144+
145+
private SetObjectDelegate CreateSetter()
146+
{
147+
var d = new DynamicMethod($"Set{_info.Name}_Generated", typeof(void), new[] {typeof(object), typeof(object)}, _info.DeclaringType);
148+
var il = d.GetILGenerator();
149+
il.Emit(OpCodes.Ldarg_0); // "this"
150+
il.Emit(OpCodes.Castclass, _info.DeclaringType); // cast to the declaring type
151+
il.Emit(OpCodes.Ldarg_1); // value
152+
if (_info.FieldType.IsValueType) // unbox it if it's a value type
153+
{
154+
il.Emit(OpCodes.Unbox_Any, _info.FieldType);
155+
}
156+
il.Emit(OpCodes.Stfld, _info); // "this".{_info} = value
157+
il.Emit(OpCodes.Ret); // return
158+
return (SetObjectDelegate) d.CreateDelegate(typeof(SetObjectDelegate));
159+
}
85160
}

UnityMapper/LibmapperDevice.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ private System.Collections.Generic.List<IMappedProperty> CreateMapping(Component
241241
{
242242
var baseType = CreateLibmapperTypeFromPrimitive(prop.FieldType);
243243
if (baseType == Mapper.Type.Null && !_converters.ContainsKey(prop.FieldType)) continue;
244-
var mapped = new MappedClassField(prop, target);
244+
var mapped = new RCGClassField(prop, target);
245245

246246
if (baseType == Mapper.Type.Null) // this type needs to be wrapped in order to be turned into a signal
247247
{

0 commit comments

Comments
 (0)