// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; using System.Dynamic; using System.Reflection; using Microsoft.ClearScript.Util; namespace Microsoft.ClearScript { internal interface IHostVariable { Type Type { get; } object Value { get; set; } } internal abstract class HostVariableBase : HostTarget { private static readonly string[] auxPropertyNames = { "out", "ref", "value" }; public override string[] GetAuxPropertyNames(IHostInvokeContext context, BindingFlags bindFlags) { return auxPropertyNames; } } internal sealed class HostVariable : HostVariableBase, IHostVariable { public HostVariable(T initValue) { if ((typeof(T) == typeof(Undefined)) || (typeof(T) == typeof(VoidResult))) { throw new NotSupportedException("Unsupported variable type"); } if (typeof(HostItem).IsAssignableFrom(typeof(T)) || typeof(HostTarget).IsAssignableFrom(typeof(T))) { throw new NotSupportedException("Unsupported variable type"); } if ((initValue is HostItem) || (initValue is HostTarget)) { throw new NotSupportedException("Unsupported value type"); } Value = initValue; } public T Value { get; set; } // Be careful when renaming or deleting this property; it is accessed by name in the // expression tree construction code in DelegateFactory.CreateComplexDelegate(). #region Object overrides public override string ToString() { var objectName = Value.GetFriendlyName(typeof(T)); return MiscHelpers.FormatInvariant("HostVariable:{0}", objectName); } #endregion #region HostTarget overrides public override Type Type => typeof(T); public override object Target => Value; public override object InvokeTarget => Value; public override object DynamicInvokeTarget => Value; public override HostTargetFlags GetFlags(IHostInvokeContext context) { var flags = HostTargetFlags.AllowInstanceMembers | HostTargetFlags.AllowExtensionMethods; if (context.Engine.ExposeHostObjectStaticMembers) { flags |= HostTargetFlags.AllowStaticMembers; } return flags; } public override bool TryInvokeAuxMember(IHostInvokeContext context, string name, BindingFlags invokeFlags, object[] args, object[] bindArgs, out object result) { const BindingFlags getPropertyFlags = BindingFlags.GetField | BindingFlags.GetProperty; const BindingFlags setPropertyFlags = BindingFlags.SetProperty | BindingFlags.PutDispProperty | BindingFlags.PutRefDispProperty; if (name == "out") { if ((invokeFlags & getPropertyFlags) != 0) { result = new OutArg(this); return true; } } else if (name == "ref") { if ((invokeFlags & getPropertyFlags) != 0) { result = new RefArg(this); return true; } } else if (name == "value") { if (invokeFlags.HasFlag(BindingFlags.InvokeMethod)) { if (InvokeHelpers.TryInvokeObject(context, Value, invokeFlags, args, bindArgs, typeof(IDynamicMetaObjectProvider).IsAssignableFrom(typeof(T)), out result)) { return true; } if (invokeFlags.HasFlag(BindingFlags.GetField) && (args.Length < 1)) { result = context.Engine.PrepareResult(Value, ScriptMemberFlags.None, false); return true; } result = null; return false; } if ((invokeFlags & getPropertyFlags) != 0) { result = context.Engine.PrepareResult(Value, ScriptMemberFlags.None, false); return true; } if ((invokeFlags & setPropertyFlags) != 0) { if (args.Length == 1) { result = context.Engine.PrepareResult(((IHostVariable)this).Value = args[0], typeof(T), ScriptMemberFlags.None, false); return true; } } } result = null; return false; } public override Invocability GetInvocability(BindingFlags bindFlags, Type accessContext, ScriptAccess defaultAccess, bool ignoreDynamic) { return typeof(T).GetInvocability(bindFlags, accessContext, defaultAccess, ignoreDynamic); } #endregion #region IHostVariable implementation object IHostVariable.Value { get => Value; set { if (!typeof(T).IsAssignableFrom(ref value)) { throw new InvalidOperationException("Assignment invalid due to type mismatch"); } if ((value is HostItem) || (value is HostTarget)) { throw new NotSupportedException("Unsupported value type"); } Value = (T)value; } } #endregion } }