// 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 /// /// 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(); } /// /// 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, "target"); object result; if (target.GetMetaObject(Expression.Constant(target)).TryCreateInstance(args, out 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); } /// /// 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(T)) { return new HostVariable(initValue); } /// /// 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 == 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()) { throw new InvalidOperationException(MiscHelpers.FormatInvariant("{0} is not a flag set type", type.GetFullFriendlyName())); } try { return args.Aggregate(0UL, (flags, arg) => flags | Convert.ToUInt64(arg, CultureInfo.InvariantCulture)).DynamicCast(); } catch (OverflowException) { return args.Aggregate(0L, (flags, arg) => flags | Convert.ToInt64(arg, CultureInfo.InvariantCulture)).DynamicCast(); } } /// /// 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, "target"); object result; if (target.TryGetValue(name, out 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, "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, "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, "target"); object result; if (target.GetMetaObject(Expression.Constant(target)).TryGetMember(name, out 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, "target"); object result; if (target.GetMetaObject(Expression.Constant(target)).TrySetMember(name, value, out 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, "target"); bool result; if (target.GetMetaObject(Expression.Constant(target)).TryDeleteMember(name, out 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, "target"); object result; if (target.GetMetaObject(Expression.Constant(target)).TryGetIndex(indices, out 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, "target"); object result; if (target.GetMetaObject(Expression.Constant(target)).TrySetIndex(indices, value, out 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, "target"); bool result; if (target.GetMetaObject(Expression.Constant(target)).TryDeleteIndex(indices, out 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, "tryFunc"); MiscHelpers.VerifyNonNullArgument(catchFunc, "catchFunc"); try { ((dynamic)tryFunc)(); return true; } catch (Exception exception) { if (!((dynamic)catchFunc)(exception)) { throw; } return false; } finally { if (finallyFunc != null) { ((dynamic)finallyFunc)(); } } } // ReSharper restore InconsistentNaming #endregion internal ScriptEngine GetEngine() { var activeEngine = ScriptEngine.Current ?? engine; if (activeEngine == 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 == 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, "engine"); this.engine = engine; } // ReSharper restore ParameterHidesMember #endregion } /// /// Provides optional script-callable utility functions. This extended version allows script /// code to import host types. /// public class ExtendedHostFunctions : HostFunctions { // ReSharper disable EmptyConstructor /// /// Initializes a new instance. /// public ExtendedHostFunctions() { // the help file builder (SHFB) insists on an empty constructor here } // ReSharper restore EmptyConstructor #region script-callable interface // ReSharper disable InconsistentNaming /// /// Imports a host type by name. /// /// The fully qualified name of the host type to import. /// Optional generic type arguments. /// The imported host type. /// /// /// Host types are imported in the form of objects whose properties and methods are bound /// to the host type's static members and nested types. If refers /// to a generic type, the corresponding object will be invocable with type arguments to /// yield a specific type. /// /// /// For more information about the mapping between host members and script-callable /// properties and methods, see /// AddHostObject. /// /// /// /// The following code imports the /// Dictionary /// generic type and uses it to create a string dictionary. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// var DictT = host.type("System.Collections.Generic.Dictionary"); /// var StringT = host.type("System.String"); /// var dict = host.newObj(DictT(StringT, StringT)); /// /// Another way to create a string dictionary is to import the specific type directly. /// /// var StringT = host.type("System.String"); /// var StringDictT = host.type("System.Collections.Generic.Dictionary", StringT, StringT); /// var dict = host.newObj(StringDictT); /// /// public object type(string name, params object[] hostTypeArgs) { return TypeHelpers.ImportType(name, null, false, hostTypeArgs); } /// /// Imports a host type by name from the specified assembly. /// /// The fully qualified name of the host type to import. /// The name of the assembly that contains the host type to import. /// Optional generic type arguments. /// The imported host type. /// /// /// Host types are imported in the form of objects whose properties and methods are bound /// to the host type's static members and nested types. If refers /// to a generic type, the corresponding object will be invocable with type arguments to /// yield a specific type. /// /// /// For more information about the mapping between host members and script-callable /// properties and methods, see /// AddHostObject. /// /// /// /// The following code imports and uses it to create /// an array of strings. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// var EnumerableT = host.type("System.Linq.Enumerable", "System.Core"); /// var Int32T = host.type("System.Int32"); /// var StringT = host.type("System.String"); /// var SelectorT = host.type("System.Func", Int32T, StringT); /// var selector = host.del(SelectorT, function (num) { return StringT.Format("The number is {0}.", num); }); /// var array = EnumerableT.Range(0, 5).Select(selector).ToArray(); /// /// /// public object type(string name, string assemblyName, params object[] hostTypeArgs) { return TypeHelpers.ImportType(name, assemblyName, true, hostTypeArgs); } /// /// Imports the host type for the specified . /// /// The that specifies the host type to import. /// The imported host type. /// /// /// Host types are imported in the form of objects whose properties and methods are bound /// to the host type's static members and nested types. If refers /// to a generic type, the corresponding object will be invocable with type arguments to /// yield a specific type. /// /// /// For more information about the mapping between host members and script-callable /// properties and methods, see /// AddHostObject. /// /// public object type(Type type) { return HostType.Wrap(type); } /// /// Imports the host array type for the specified element type. /// /// The element type for the host array type to import. /// The number of dimensions for the host array type to import. /// The imported host array type. public object arrType(int rank = 1) { return HostType.Wrap(typeof(T).MakeArrayType(rank)); } /// /// Imports types from one or more host assemblies. /// /// The names of the assemblies that contain the types to import. /// The imported host type collection. /// /// Host type collections provide convenient scriptable access to all the types defined in one /// or more host assemblies. They are hierarchical collections where leaf nodes represent types /// and parent nodes represent namespaces. For example, if an assembly contains a type named /// "Acme.Gadgets.Button", the corresponding collection will have a property named "Acme" whose /// value is an object with a property named "Gadgets" whose value is an object with a property /// named "Button" whose value represents the Acme.Gadgets.Button host type. /// /// /// The following code imports types from several core assemblies and uses /// to create an array of integers. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// var clr = host.lib("mscorlib", "System", "System.Core"); /// var array = clr.System.Linq.Enumerable.Range(0, 5).ToArray(); /// /// public HostTypeCollection lib(params string[] assemblyNames) { return lib(null, assemblyNames); } /// /// Imports types from one or more host assemblies and merges them with an existing host type collection. /// /// The host type collection with which to merge types from the specified assemblies. /// The names of the assemblies that contain the types to import. /// A host type collection: if it is not null, a new host type collection otherwise. /// /// Host type collections provide convenient scriptable access to all the types defined in one /// or more host assemblies. They are hierarchical collections where leaf nodes represent types /// and parent nodes represent namespaces. For example, if an assembly contains a type named /// "Acme.Gadgets.Button", the corresponding collection will have a property named "Acme" whose /// value is an object with a property named "Gadgets" whose value is an object with a property /// named "Button" whose value represents the Acme.Gadgets.Button host type. /// /// /// The following code imports types from several core assemblies and uses /// to create an array of integers. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// var clr = host.lib("mscorlib"); /// host.lib(clr, "System"); /// host.lib(clr, "System.Core"); /// var array = clr.System.Linq.Enumerable.Range(0, 5).ToArray(); /// /// public HostTypeCollection lib(HostTypeCollection collection, params string[] assemblyNames) { var target = collection ?? new HostTypeCollection(); Array.ForEach(assemblyNames, target.AddAssembly); return target; } /// /// Imports a COM/ActiveX type. /// /// The programmatic identifier (ProgID) of the registered class to import. /// An optional name that specifies the server from which to import the type. /// The imported COM/ActiveX type. /// /// The argument can be a class identifier (CLSID) in standard /// GUID format with braces (e.g., "{0D43FE01-F093-11CF-8940-00A0C9054228}"). /// /// /// The following code imports the /// Scripting.Dictionary /// class and uses it to create and populate an instance. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// var DictT = host.comType('Scripting.Dictionary'); /// var dict = host.newObj(DictT); /// dict.Add('foo', 123); /// dict.Add('bar', 456.789); /// dict.Add('baz', 'abc'); /// /// public object comType(string progID, string serverName = null) { return HostType.Wrap(MiscHelpers.GetCOMType(progID, serverName)); } /// /// Creates a COM/ActiveX object of the specified type. /// /// The programmatic identifier (ProgID) of the registered class to instantiate. /// An optional name that specifies the server on which to create the object. /// A new COM/ActiveX object of the specified type. /// /// The argument can be a class identifier (CLSID) in standard /// GUID format with braces (e.g., "{0D43FE01-F093-11CF-8940-00A0C9054228}"). /// /// /// The following code creates a /// Scripting.FileSystemObject /// instance and uses it to list the drives on the local machine. /// It assumes that an instance of is exposed under /// the name "host" /// (see AddHostObject). /// /// var fso = host.newComObj('Scripting.FileSystemObject'); /// var ConsoleT = host.type('System.Console'); /// for (en = fso.Drives.GetEnumerator(); en.MoveNext();) { /// ConsoleT.WriteLine(en.Current.Path); /// } /// /// public object newComObj(string progID, string serverName = null) { return MiscHelpers.CreateCOMObject(progID, serverName); } // ReSharper restore InconsistentNaming #endregion } }