// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Globalization; using System.Linq; using System.Linq.Expressions; using Microsoft.ClearScript.Util; namespace Microsoft.ClearScript { /// /// Provides optional script-callable utility functions. /// /// /// Use AddHostObject to expose a /// HostFunctions instance to script code. Each instance can only be exposed in one /// script engine. /// public class HostFunctions : IScriptableObject { private ScriptEngine engine; // ReSharper disable EmptyConstructor /// /// Initializes a new instance. /// public HostFunctions() { // the help file builder (SHFB) insists on an empty constructor here } // ReSharper restore EmptyConstructor #region script-callable interface // ReSharper disable InconsistentNaming // ReSharper disable GrammarMistakeInComment /// /// Creates an empty host object. /// /// A new empty host object. /// /// This function is provided for script languages that do not support external /// instantiation. It creates an object that supports dynamic property addition and /// removal. The host can manipulate it via the interface. /// /// /// The following code creates an empty host object and adds several properties to it. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// var item = host.newObj(); /// item.label = "Widget"; /// item.weight = 123.45; /// /// public PropertyBag newObj() { return new PropertyBag(); } // ReSharper restore GrammarMistakeInComment /// /// Creates a host object of the specified type. This version is invoked if the specified /// type can be used as a type argument. /// /// The type of object to create. /// Optional constructor arguments. /// A new host object of the specified type. /// /// /// This function is provided for script languages that do not support external /// instantiation. It is overloaded with and /// selected at runtime if can be used as a type argument. /// /// /// For information about the mapping between host members and script-callable properties /// and methods, see /// AddHostObject. /// /// /// /// The following code imports the class, creates an /// instance using the /// Random(Int32) /// constructor, and calls the method. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// var RandomT = host.type("System.Random"); /// var random = host.newObj(RandomT, 100); /// var value = random.NextDouble(); /// /// /// public T newObj(params object[] args) { return (T)typeof(T).CreateInstance(args); } /// /// Creates a host object of the specified type. This version is invoked if the specified /// type cannot be used as a type argument. /// /// The type of object to create. /// Optional constructor arguments. /// A new host object of the specified type. /// /// /// This function is provided for script languages that do not support external /// instantiation. It is overloaded with and selected at runtime if /// cannot be used as a type argument. Note that this applies /// to some host types that support instantiation, such as certain COM/ActiveX types. /// /// /// For information about the mapping between host members and script-callable properties /// and methods, see /// AddHostObject. /// /// public object newObj(object type, params object[] args) { return GetUniqueHostType(type, "type").CreateInstance(args); } /// /// Performs dynamic instantiation. /// /// The dynamic host object that provides the instantiation operation to perform. /// Optional instantiation arguments. /// The result of the operation, which is usually a new dynamic host object. /// /// This function is provided for script languages that do not support external /// instantiation. /// public object newObj(IDynamicMetaObjectProvider target, params object[] args) { MiscHelpers.VerifyNonNullArgument(target, nameof(target)); if (target.GetMetaObject(Expression.Constant(target)).TryCreateInstance(args, out var result)) { return result; } throw new InvalidOperationException("Invalid dynamic instantiation"); } /// /// Creates a host array with the specified element type. /// /// The element type of the array to create. /// One or more integers representing the array dimension lengths. /// A new host array with the specified element type. /// /// For information about the mapping between host members and script-callable properties /// and methods, see /// AddHostObject. /// /// /// The following code creates a 5x3 host array of strings. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// var StringT = host.type("System.String"); /// var array = host.newArr(StringT, 5, 3); /// /// /// /// public object newArr(params int[] lengths) { return Array.CreateInstance(typeof(T), lengths); } /// /// Creates a host array with as the element type. /// /// One or more integers representing the array dimension lengths. /// A new host array with as the element type. /// /// For information about the mapping between host members and script-callable properties /// and methods, see /// AddHostObject. /// /// public object newArr(params int[] lengths) { return newArr(lengths); } // ReSharper disable GrammarMistakeInComment /// /// Creates a host variable of the specified type. /// /// The type of variable to create. /// An optional initial value for the variable. /// A new host variable of the specified type. /// /// /// A host variable is a strongly typed object that holds a value of the specified type. /// Host variables are useful for passing method arguments by reference. In addition to /// being generally interchangeable with their stored values, host variables support the /// following properties: /// /// /// /// /// Property /// Access /// Description /// /// /// value /// read-write /// The current value of the host variable. /// /// /// out /// read-only /// A reference to the host variable that can be passed as an out argument. /// /// /// ref /// read-only /// A reference to the host variable that can be passed as a ref argument. /// /// /// /// /// /// The following code demonstrates using a host variable to invoke a method with an /// out parameter. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import a dictionary type /// var StringT = host.type("System.String"); /// var StringDictT = host.type("System.Collections.Generic.Dictionary", StringT, StringT); /// // create and populate a dictionary /// var dict = host.newObj(StringDictT); /// dict.Add("foo", "bar"); /// dict.Add("baz", "qux"); /// // look up a dictionary entry /// var result = host.newVar(StringT); /// var found = dict.TryGetValue("baz", result.out); /// /// /// public object newVar(T initValue = default) { return new HostVariable(initValue); } // ReSharper restore GrammarMistakeInComment /// /// Creates a delegate that invokes a script function. /// /// The type of delegate to create. /// The script function for which to create a delegate. /// A new delegate that invokes the specified script function. /// /// If the delegate signature includes parameters passed by reference, the corresponding /// arguments to the script function will be host variables. /// The script function can set the value of an output argument by assigning the /// corresponding host variable's value property. /// /// /// The following code demonstrates delegating a callback to a script function. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // create and populate an array of integers /// var EnumerableT = host.type("System.Linq.Enumerable", "System.Core"); /// var array = EnumerableT.Range(1, 5).ToArray(); /// // import the callback type required to call Array.ForEach /// var Int32T = host.type("System.Int32"); /// var CallbackT = host.type("System.Action", Int32T); /// // use Array.ForEach to calculate a sum /// var sum = 0; /// var ArrayT = host.type("System.Array"); /// ArrayT.ForEach(array, host.del(CallbackT, function (value) { sum += value; })); /// /// /// /// public T del(object scriptFunc) { return DelegateFactory.CreateDelegate(GetEngine(), scriptFunc); } /// /// Creates a delegate that invokes a script function and returns no value. /// /// The number of arguments to pass to the script function. /// The script function for which to create a delegate. /// A new delegate that invokes the specified script function and returns no value. /// /// This function creates a delegate that accepts arguments and /// returns no value. The type of all parameters is . Such a /// delegate is often useful in strongly typed contexts because of /// contravariance. /// /// /// The following code demonstrates delegating a callback to a script function. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // create and populate an array of strings /// var StringT = host.type("System.String"); /// var array = host.newArr(StringT, 3); /// array.SetValue("first", 0); /// array.SetValue("second", 1); /// array.SetValue("third", 2); /// // use Array.ForEach to generate console output /// var ArrayT = host.type("System.Array"); /// var ConsoleT = host.type("System.Console"); /// ArrayT.ForEach(array, host.proc(1, function (value) { ConsoleT.WriteLine(value); })); /// /// /// /// public object proc(int argCount, object scriptFunc) { return DelegateFactory.CreateProc(GetEngine(), scriptFunc, argCount); } /// /// Creates a delegate that invokes a script function and returns a value of the specified type. /// /// The return value type. /// The number of arguments to pass to the script function. /// The script function for which to create a delegate. /// A new delegate that invokes the specified script function and returns a value of the specified type. /// /// This function creates a delegate that accepts arguments and /// returns a value of the specified type. The type of all parameters is /// . Such a delegate is often useful in strongly typed contexts /// because of /// contravariance. /// /// /// The following code demonstrates delegating a callback to a script function. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // create and populate an array of strings /// var StringT = host.type("System.String"); /// var array = host.newArr(StringT, 3); /// array.SetValue("first", 0); /// array.SetValue("second", 1); /// array.SetValue("third", 2); /// // import LINQ extensions /// var EnumerableT = host.type("System.Linq.Enumerable", "System.Core"); /// // use LINQ to create an array of modified strings /// var selector = host.func(StringT, 1, function (value) { return value.toUpperCase(); }); /// array = array.Select(selector).ToArray(); /// /// /// /// /// public object func(int argCount, object scriptFunc) { return DelegateFactory.CreateFunc(GetEngine(), scriptFunc, argCount); } /// /// Creates a delegate that invokes a script function and returns its result value. /// /// The number of arguments to pass to the script function. /// The script function for which to create a delegate. /// A new delegate that invokes the specified script function and returns its result value. /// /// /// This function creates a delegate that accepts arguments and /// returns the result of invoking . The type of all /// parameters and the return value is . Such a delegate is /// often useful in strongly typed contexts because of /// contravariance. /// /// /// For information about the types of result values that script code can return, see /// . /// /// /// public object func(int argCount, object scriptFunc) { return func(argCount, scriptFunc); } /// /// Gets the for the specified host type. This version is invoked /// if the specified object can be used as a type argument. /// /// The host type for which to get the . /// The for the specified host type. /// /// /// This function is similar to C#'s /// typeof /// operator. It is overloaded with and selected at runtime if /// can be used as a type argument. /// /// /// This function throws an exception if the script engine's /// property is set to false. /// /// /// /// The following code retrieves the assembly-qualified name of a host type. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// var StringT = host.type("System.String"); /// var name = host.typeOf(StringT).AssemblyQualifiedName; /// /// /// public Type typeOf() { GetEngine().CheckReflection(); return typeof(T); } /// /// Gets the for the specified host type. This version is invoked /// if the specified object cannot be used as a type argument. /// /// The host type for which to get the . /// The for the specified host type. /// /// /// This function is similar to C#'s /// typeof /// operator. It is overloaded with and selected at runtime if /// cannot be used as a type argument. Note that this applies to /// some host types; examples are static types and overloaded generic type groups. /// /// /// This function throws an exception if the script engine's /// property is set to false. /// /// /// /// The following code retrieves the assembly-qualified name of a host type. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// var ConsoleT = host.type("System.Console"); /// var name = host.typeOf(ConsoleT).AssemblyQualifiedName; /// /// /// public Type typeOf(object value) { GetEngine().CheckReflection(); return GetUniqueHostType(value, "value"); } /// /// Determines whether an object is compatible with the specified host type. /// /// The host type with which to test for compatibility. /// The object to test for compatibility with the specified host type. /// True if is compatible with the specified type, false otherwise. /// /// This function is similar to C#'s /// is /// operator. /// /// /// The following code defines a function that determines whether an object implements /// . /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// function isComparable(value) /// { /// var IComparableT = host.type("System.IComparable"); /// return host.isType(IComparableT, value); /// } /// /// /// public bool isType(object value) { return value is T; } /// /// Casts an object to the specified host type, returning null if the cast fails. /// /// The host type to which to cast . /// The object to cast to the specified host type. /// The result of the cast if successful, null otherwise. /// /// This function is similar to C#'s /// as /// operator. /// /// /// The following code defines a function that disposes an object if it implements /// . /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// function dispose(value) /// { /// var IDisposableT = host.type("System.IDisposable"); /// var disposable = host.asType(IDisposableT, value); /// if (disposable) { /// disposable.Dispose(); /// } /// } /// /// /// public object asType(object value) where T : class { return HostItem.Wrap(GetEngine(), value as T, typeof(T)); } /// /// Casts an object to the specified host type. /// /// The host type to which to cast . /// The object to cast to the specified host type. /// The result of the cast. /// /// If the cast fails, this function throws an exception. /// /// /// The following code casts a floating-point value to a 32-bit integer. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// var Int32T = host.type("System.Int32"); /// var intValue = host.cast(Int32T, 12.5); /// /// /// public object cast(object value) { return HostItem.Wrap(GetEngine(), value.DynamicCast(), typeof(T)); } /// /// Determines whether an object is a host type. This version is invoked if the specified /// object cannot be used as a type argument. /// /// The object to test. /// True if is a host type, false otherwise. /// /// This function is overloaded with and selected at runtime if /// cannot be used as a type argument. Note that this applies to /// some host types; examples are static types and overloaded generic type groups. /// public bool isTypeObj(object value) { return value is HostType; } // ReSharper disable UnusedTypeParameter /// /// Determines whether an object is a host type. This version is invoked if the specified /// object can be used as a type argument. /// /// The host type (ignored). /// True. /// /// This function is overloaded with and selected at /// runtime if can be used as a type argument. Because type /// arguments are always host types, this method ignores its type argument and always /// returns true. /// public bool isTypeObj() { return true; } // ReSharper restore UnusedTypeParameter /// /// Determines whether the specified value is null. /// /// The value to test. /// True if is null, false otherwise. /// /// Use this function to test field, property, and method return values when null /// result wrapping is in effect (see /// and /// ). /// /// /// public bool isNull(object value) { return value is null; } /// /// Creates a strongly typed flag set. /// /// The type of flag set to create. /// The flags to include in the flag set. /// A strongly typed flag set containing the specified flags. /// /// This function throws an exception if is not a flag set type. /// /// /// The following code demonstrates using a strongly typed flag set. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import URI types /// var UriT = host.type("System.Uri", "System"); /// var UriFormatT = host.type("System.UriFormat", "System"); /// var UriComponentsT = host.type("System.UriComponents", "System"); /// // create a URI /// var uri = host.newObj(UriT, "http://www.example.com:8080/path/to/file/sample.htm?x=1&y=2"); /// // extract URI components /// var components = host.flags(UriComponentsT.Scheme, UriComponentsT.Host, UriComponentsT.Path); /// var result = uri.GetComponents(components, UriFormatT.Unescaped); /// /// /// public T flags(params T[] args) { var type = typeof(T); if (!type.IsFlagsEnum(GetEngine())) { throw new InvalidOperationException(MiscHelpers.FormatInvariant("{0} is not a flag set type", type.GetFullFriendlyName())); } try { return (T)Enum.ToObject(typeof(T), args.Aggregate(0UL, (flags, arg) => flags | Convert.ToUInt64(arg, CultureInfo.InvariantCulture))); } catch (OverflowException) { return (T)Enum.ToObject(typeof(T), args.Aggregate(0L, (flags, arg) => flags | Convert.ToInt64(arg, CultureInfo.InvariantCulture))); } } /// /// Converts the specified value to a strongly typed instance. /// /// The value to convert. /// An object that can be passed to a parameter of type . /// /// This function converts to and /// packages the result to retain its numeric type across the host-script boundary. It may /// be useful for passing arguments to parameters if the script /// engine does not support that type natively. /// /// /// The following code adds an element of type to a strongly /// typed list. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import types /// var ElementT = host.type("System.SByte"); /// var ListT = host.type("System.Collections.Generic.List", ElementT); /// // create a list /// var list = host.newObj(ListT); /// // add a list element /// list.Add(host.toSByte(42)); /// /// /// public object toSByte(IConvertible value) { return HostObject.Wrap(Convert.ToSByte(value)); } /// /// Converts the specified value to a strongly typed instance. /// /// The value to convert. /// An object that can be passed to a parameter of type . /// /// This function converts to and /// packages the result to retain its numeric type across the host-script boundary. It may /// be useful for passing arguments to parameters if the script /// engine does not support that type natively. /// /// /// The following code adds an element of type to a strongly /// typed list. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import types /// var ElementT = host.type("System.Byte"); /// var ListT = host.type("System.Collections.Generic.List", ElementT); /// // create a list /// var list = host.newObj(ListT); /// // add a list element /// list.Add(host.toByte(42)); /// /// /// public object toByte(IConvertible value) { return HostObject.Wrap(Convert.ToByte(value)); } /// /// Converts the specified value to a strongly typed instance. /// /// The value to convert. /// An object that can be passed to a parameter of type . /// /// This function converts to and /// packages the result to retain its numeric type across the host-script boundary. It may /// be useful for passing arguments to parameters if the script /// engine does not support that type natively. /// /// /// The following code adds an element of type to a strongly /// typed list. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import types /// var ElementT = host.type("System.Int16"); /// var ListT = host.type("System.Collections.Generic.List", ElementT); /// // create a list /// var list = host.newObj(ListT); /// // add a list element /// list.Add(host.toInt16(42)); /// /// /// public object toInt16(IConvertible value) { return HostObject.Wrap(Convert.ToInt16(value)); } /// /// Converts the specified value to a strongly typed instance. /// /// The value to convert. /// An object that can be passed to a parameter of type . /// /// This function converts to and /// packages the result to retain its numeric type across the host-script boundary. It may /// be useful for passing arguments to parameters if the script /// engine does not support that type natively. /// /// /// The following code adds an element of type to a strongly /// typed list. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import types /// var ElementT = host.type("System.UInt16"); /// var ListT = host.type("System.Collections.Generic.List", ElementT); /// // create a list /// var list = host.newObj(ListT); /// // add a list element /// list.Add(host.toUInt16(42)); /// /// /// public object toUInt16(IConvertible value) { return HostObject.Wrap(Convert.ToUInt16(value)); } /// /// Converts the specified value to a strongly typed instance. /// /// The value to convert. /// An object that can be passed to a parameter of type . /// /// This function converts to and /// packages the result to retain its numeric type across the host-script boundary. It may /// be useful for passing arguments to parameters if the script /// engine does not support that type natively. /// /// /// The following code adds an element of type to a strongly /// typed list. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import types /// var ElementT = host.type("System.Char"); /// var ListT = host.type("System.Collections.Generic.List", ElementT); /// // create a list /// var list = host.newObj(ListT); /// // add a list element /// list.Add(host.toChar(42)); /// /// /// public object toChar(IConvertible value) { return HostObject.Wrap(Convert.ToChar(value)); } /// /// Converts the specified value to a strongly typed instance. /// /// The value to convert. /// An object that can be passed to a parameter of type . /// /// This function converts to and /// packages the result to retain its numeric type across the host-script boundary. It may /// be useful for passing arguments to parameters if the script /// engine does not support that type natively. /// /// /// The following code adds an element of type to a strongly /// typed list. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import types /// var ElementT = host.type("System.Int32"); /// var ListT = host.type("System.Collections.Generic.List", ElementT); /// // create a list /// var list = host.newObj(ListT); /// // add a list element /// list.Add(host.toInt32(42)); /// /// /// public object toInt32(IConvertible value) { return HostObject.Wrap(Convert.ToInt32(value)); } /// /// Converts the specified value to a strongly typed instance. /// /// The value to convert. /// An object that can be passed to a parameter of type . /// /// This function converts to and /// packages the result to retain its numeric type across the host-script boundary. It may /// be useful for passing arguments to parameters if the script /// engine does not support that type natively. /// /// /// The following code adds an element of type to a strongly /// typed list. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import types /// var ElementT = host.type("System.UInt32"); /// var ListT = host.type("System.Collections.Generic.List", ElementT); /// // create a list /// var list = host.newObj(ListT); /// // add a list element /// list.Add(host.toUInt32(42)); /// /// /// public object toUInt32(IConvertible value) { return HostObject.Wrap(Convert.ToUInt32(value)); } /// /// Converts the specified value to a strongly typed instance. /// /// The value to convert. /// An object that can be passed to a parameter of type . /// /// This function converts to and /// packages the result to retain its numeric type across the host-script boundary. It may /// be useful for passing arguments to parameters if the script /// engine does not support that type natively. /// /// /// The following code adds an element of type to a strongly /// typed list. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import types /// var ElementT = host.type("System.Int64"); /// var ListT = host.type("System.Collections.Generic.List", ElementT); /// // create a list /// var list = host.newObj(ListT); /// // add a list element /// list.Add(host.toInt64(42)); /// /// /// public object toInt64(IConvertible value) { return HostObject.Wrap(Convert.ToInt64(value)); } /// /// Converts the specified value to a strongly typed instance. /// /// The value to convert. /// An object that can be passed to a parameter of type . /// /// This function converts to and /// packages the result to retain its numeric type across the host-script boundary. It may /// be useful for passing arguments to parameters if the script /// engine does not support that type natively. /// /// /// The following code adds an element of type to a strongly /// typed list. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import types /// var ElementT = host.type("System.UInt64"); /// var ListT = host.type("System.Collections.Generic.List", ElementT); /// // create a list /// var list = host.newObj(ListT); /// // add a list element /// list.Add(host.toUInt64(42)); /// /// /// public object toUInt64(IConvertible value) { return HostObject.Wrap(Convert.ToUInt64(value)); } /// /// Converts the specified value to a strongly typed instance. /// /// The value to convert. /// An object that can be passed to a parameter of type . /// /// This function converts to and /// packages the result to retain its numeric type across the host-script boundary. It may /// be useful for passing arguments to parameters if the script /// engine does not support that type natively. /// /// /// The following code adds an element of type to a strongly /// typed list. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import types /// var ElementT = host.type("System.Single"); /// var ListT = host.type("System.Collections.Generic.List", ElementT); /// // create a list /// var list = host.newObj(ListT); /// // add a list element /// list.Add(host.toSingle(42)); /// /// /// public object toSingle(IConvertible value) { return HostObject.Wrap(Convert.ToSingle(value)); } /// /// Converts the specified value to a strongly typed instance. /// /// The value to convert. /// An object that can be passed to a parameter of type . /// /// This function converts to and /// packages the result to retain its numeric type across the host-script boundary. It may /// be useful for passing arguments to parameters if the script /// engine does not support that type natively. /// /// /// The following code adds an element of type to a strongly /// typed list. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import types /// var ElementT = host.type("System.Double"); /// var ListT = host.type("System.Collections.Generic.List", ElementT); /// // create a list /// var list = host.newObj(ListT); /// // add a list element /// list.Add(host.toDouble(42)); /// /// /// public object toDouble(IConvertible value) { return HostObject.Wrap(Convert.ToDouble(value)); } /// /// Converts the specified value to a strongly typed instance. /// /// The value to convert. /// An object that can be passed to a parameter of type . /// /// This function converts to and /// packages the result to retain its numeric type across the host-script boundary. It may /// be useful for passing arguments to parameters if the script /// engine does not support that type natively. /// /// /// The following code adds an element of type to a strongly /// typed list. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import types /// var ElementT = host.type("System.Decimal"); /// var ListT = host.type("System.Collections.Generic.List", ElementT); /// // create a list /// var list = host.newObj(ListT); /// // add a list element /// list.Add(host.toDecimal(42)); /// /// /// public object toDecimal(IConvertible value) { return HostObject.Wrap(Convert.ToDecimal(value)); } /// /// Gets the value of a property in a dynamic host object that implements . /// /// The dynamic host object that contains the property to get. /// The name of the property to get. /// The value of the specified property. /// /// This function is provided for script languages that do not support dynamic properties. /// public object getProperty(IPropertyBag target, string name) { MiscHelpers.VerifyNonNullArgument(target, nameof(target)); if (target.TryGetValue(name, out var result)) { return result; } return Nonexistent.Value; } /// /// Sets a property value in a dynamic host object that implements . /// /// The dynamic host object that contains the property to set. /// The name of the property to set. /// The new value of the specified property. /// The result of the operation, which is usually the value assigned to the specified property. /// /// This function is provided for script languages that do not support dynamic properties. /// public object setProperty(IPropertyBag target, string name, object value) { MiscHelpers.VerifyNonNullArgument(target, nameof(target)); return target[name] = value; } /// /// Removes a property from a dynamic host object that implements . /// /// The dynamic host object that contains the property to remove. /// The name of the property to remove. /// True if the property was found and removed, false otherwise. /// /// This function is provided for script languages that do not support dynamic properties. /// public bool removeProperty(IPropertyBag target, string name) { MiscHelpers.VerifyNonNullArgument(target, nameof(target)); return target.Remove(name); } /// /// Gets the value of a property in a dynamic host object that implements . /// /// The dynamic host object that contains the property to get. /// The name of the property to get. /// The value of the specified property. /// /// This function is provided for script languages that do not support dynamic properties. /// public object getProperty(IDynamicMetaObjectProvider target, string name) { MiscHelpers.VerifyNonNullArgument(target, nameof(target)); if (target.GetMetaObject(Expression.Constant(target)).TryGetMember(name, out var result)) { return result; } return Nonexistent.Value; } /// /// Sets a property value in a dynamic host object that implements . /// /// The dynamic host object that contains the property to set. /// The name of the property to set. /// The new value of the specified property. /// The result of the operation, which is usually the value assigned to the specified property. /// /// This function is provided for script languages that do not support dynamic properties. /// public object setProperty(IDynamicMetaObjectProvider target, string name, object value) { MiscHelpers.VerifyNonNullArgument(target, nameof(target)); if (target.GetMetaObject(Expression.Constant(target)).TrySetMember(name, value, out var result)) { return result; } throw new InvalidOperationException("Invalid dynamic property assignment"); } /// /// Removes a property from a dynamic host object that implements . /// /// The dynamic host object that contains the property to remove. /// The name of the property to remove. /// True if the property was found and removed, false otherwise. /// /// This function is provided for script languages that do not support dynamic properties. /// public bool removeProperty(IDynamicMetaObjectProvider target, string name) { MiscHelpers.VerifyNonNullArgument(target, nameof(target)); if (target.GetMetaObject(Expression.Constant(target)).TryDeleteMember(name, out var result)) { return result; } throw new InvalidOperationException("Invalid dynamic property deletion"); } /// /// Gets the value of an element in a dynamic host object that implements . /// /// The dynamic host object that contains the element to get. /// One or more indices that identify the element to get. /// The value of the specified element. /// /// This function is provided for script languages that do not support general indexing. /// public object getElement(IDynamicMetaObjectProvider target, params object[] indices) { MiscHelpers.VerifyNonNullArgument(target, nameof(target)); if (target.GetMetaObject(Expression.Constant(target)).TryGetIndex(indices, out var result)) { return result; } return Nonexistent.Value; } /// /// Sets an element value in a dynamic host object that implements . /// /// The dynamic host object that contains the element to set. /// The new value of the element. /// One or more indices that identify the element to set. /// The result of the operation, which is usually the value assigned to the specified element. /// /// This function is provided for script languages that do not support general indexing. /// public object setElement(IDynamicMetaObjectProvider target, object value, params object[] indices) { MiscHelpers.VerifyNonNullArgument(target, nameof(target)); if (target.GetMetaObject(Expression.Constant(target)).TrySetIndex(indices, value, out var result)) { return result; } throw new InvalidOperationException("Invalid dynamic element assignment"); } /// /// Removes an element from a dynamic host object that implements . /// /// The dynamic host object that contains the element to remove. /// One or more indices that identify the element to remove. /// True if the element was found and removed, false otherwise. /// /// This function is provided for script languages that do not support general indexing. /// public bool removeElement(IDynamicMetaObjectProvider target, params object[] indices) { MiscHelpers.VerifyNonNullArgument(target, nameof(target)); if (target.GetMetaObject(Expression.Constant(target)).TryDeleteIndex(indices, out var result)) { return result; } throw new InvalidOperationException("Invalid dynamic element deletion"); } /// /// Casts a dynamic host object to its static type. /// /// The object to cast to its static type. /// The specified object in its static type form, stripped of its dynamic members. /// /// A dynamic host object that implements may have /// dynamic members that override members of its static type. This function can be used to /// gain access to type members overridden in this manner. /// public object toStaticType(IDynamicMetaObjectProvider value) { return HostItem.Wrap(GetEngine(), value, HostItemFlags.HideDynamicMembers); } /// /// Allows script code to handle host exceptions. /// /// A script function that invokes one or more host methods or properties. /// A script function to invoke if throws an exception. /// An optional script function that performs cleanup for the operation. /// True if completed successfully, false if it threw an exception that was handled by . /// /// This function uses a try-catch-finally statement to invoke /// . If an exception is thrown, it is caught and passed to /// for analysis. If returns /// false, the exception is rethrown. Regardless of the outcome, /// , if specified, is invoked as a final step before the /// function exits. /// /// /// The following code demonstrates handling host exceptions in script code. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// // import types /// var ConsoleT = host.type("System.Console"); /// var WebClientT = host.type("System.Net.WebClient", "System"); /// // create a Web client /// var webClient = host.newObj(WebClientT); /// host.tryCatch( /// function () { /// // download Web document /// ConsoleT.WriteLine(webClient.DownloadString("http://cnn.com")); /// }, /// function (exception) { /// // dump exception /// ConsoleT.WriteLine("*** ERROR: " + exception.GetBaseException().ToString()); /// return true; /// }, /// function () { /// // clean up /// ConsoleT.WriteLine("*** CLEANING UP ***"); /// webClient.Dispose(); /// } /// ); /// /// /// /// public bool tryCatch(object tryFunc, object catchFunc, object finallyFunc = null) { MiscHelpers.VerifyNonNullArgument(tryFunc, nameof(tryFunc)); MiscHelpers.VerifyNonNullArgument(catchFunc, nameof(catchFunc)); try { ((ScriptObject)tryFunc).InvokeAsFunction(); return true; } catch (Exception exception) { if (!Convert.ToBoolean(((ScriptObject)catchFunc).InvokeAsFunction(exception))) { throw; } return false; } finally { if (finallyFunc is not null) { ((ScriptObject)finallyFunc).InvokeAsFunction(); } } } // ReSharper restore InconsistentNaming #endregion internal ScriptEngine GetEngine() { var activeEngine = engine ?? ScriptEngine.Current; if (activeEngine is null) { throw new InvalidOperationException("Operation requires a script engine"); } return activeEngine; } internal static Type GetUniqueHostType(object type, string paramName) { var hostType = type as HostType; if (hostType is null) { throw new ArgumentException("Invalid host type", paramName); } if (hostType.Types.Length > 1) { throw new ArgumentException(MiscHelpers.FormatInvariant("'{0}' does not identify a unique host type", hostType.Types[0].GetLocator()), paramName); } return hostType.Types[0]; } #region IScriptableObject implementation // ReSharper disable ParameterHidesMember [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "This member is not expected to be re-implemented in derived classes.")] void IScriptableObject.OnExposedToScriptCode(ScriptEngine engine) { MiscHelpers.VerifyNonNullArgument(engine, nameof(engine)); this.engine = engine; } // ReSharper restore ParameterHidesMember #endregion } }