aspclassic-core/AspClassic.VBScript/Runtime/DynamicObjectHelpers.cs
Jelle Luteijn 484dbfc9d9 progress
2022-05-15 11:19:49 +02:00

118 lines
4.8 KiB
C#

using System;
using System.Collections.Generic;
//using System.Text;
using System.Reflection;
using System.Dynamic;
#if USE35
using AspClassic.Scripting.Ast;
using AspClassic.Scripting.Utils;
#else
using System.Linq.Expressions;
#endif
using System.Runtime.CompilerServices;
namespace Dlrsoft.VBScript.Runtime
{
//#####################################################
//# Dynamic Helpers for HasMember, GetMember, SetMember
//#####################################################
// DynamicObjectHelpers provides access to IDynObj members given names as
// data at runtime. When the names are known at compile time (o.foo), then
// they get baked into specific sites with specific binders that encapsulate
// the name. We need this in python because hasattr et al are case-sensitive.
//
class DynamicObjectHelpers
{
static private object _sentinel = new object();
static internal object Sentinel { get { return _sentinel; } }
internal static bool HasMember(IDynamicMetaObjectProvider o,
string name)
{
return (DynamicObjectHelpers.GetMember(o, name) !=
DynamicObjectHelpers.Sentinel);
//Alternative impl used when EOs had bug and didn't call fallback ...
//var mo = o.GetMetaObject(Expression.Parameter(typeof(object), null));
//foreach (string member in mo.GetDynamicMemberNames()) {
// if (string.Equals(member, name, StringComparison.OrdinalIgnoreCase)) {
// return true;
// }
//}
//return false;
}
static private Dictionary<string,
CallSite<AspClassic.Scripting.Utils.Func<CallSite, object, object>>>
_getSites = new Dictionary<string,
CallSite<AspClassic.Scripting.Utils.Func<CallSite, object, object>>>();
internal static object GetMember(IDynamicMetaObjectProvider o,
string name)
{
CallSite<AspClassic.Scripting.Utils.Func<CallSite, object, object>> site;
if (!DynamicObjectHelpers._getSites.TryGetValue(name, out site))
{
site = CallSite<AspClassic.Scripting.Utils.Func<CallSite, object, object>>
.Create(new DoHelpersGetMemberBinder(name));
DynamicObjectHelpers._getSites[name] = site;
}
return site.Target(site, o);
}
static private Dictionary<string,
CallSite<AspClassic.Scripting.Utils.Action<CallSite, object, object>>>
_setSites = new Dictionary<string,
CallSite<AspClassic.Scripting.Utils.Action<CallSite, object, object>>>();
internal static void SetMember(IDynamicMetaObjectProvider o, string name,
object value)
{
CallSite<AspClassic.Scripting.Utils.Action<CallSite, object, object>> site;
if (!DynamicObjectHelpers._setSites.TryGetValue(name, out site))
{
site = CallSite<AspClassic.Scripting.Utils.Action<CallSite, object, object>>
.Create(new DoHelpersSetMemberBinder(name));
DynamicObjectHelpers._setSites[name] = site;
}
site.Target(site, o, value);
}
}
class DoHelpersGetMemberBinder : GetMemberBinder
{
internal DoHelpersGetMemberBinder(string name) : base(name, true) { }
public override DynamicMetaObject FallbackGetMember(
DynamicMetaObject target, DynamicMetaObject errorSuggestion)
{
return errorSuggestion ??
new DynamicMetaObject(
Expression.Constant(DynamicObjectHelpers.Sentinel),
target.Restrictions.Merge(
BindingRestrictions.GetTypeRestriction(
target.Expression, target.LimitType)));
}
}
class DoHelpersSetMemberBinder : SetMemberBinder
{
internal DoHelpersSetMemberBinder(string name) : base(name, true) { }
public override DynamicMetaObject FallbackSetMember(
DynamicMetaObject target, DynamicMetaObject value,
DynamicMetaObject errorSuggestion)
{
return errorSuggestion ??
RuntimeHelpers.CreateThrow(
target, null, BindingRestrictions.Empty,
typeof(MissingMemberException),
"If IDynObj doesn't support setting members, " +
"DOHelpers can't do it for the IDO.");
}
}
}