/* **************************************************************************** * * Copyright (c) Microsoft Corporation. * * This source code is subject to terms and conditions of the Microsoft Public License. A * copy of the license can be found in the License.html file at the root of this distribution. If * you cannot locate the Microsoft Public License, please send an email to * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound * by the terms of the Microsoft Public License. * * You must not remove this notice, or any other, from this software. * * * ***************************************************************************/ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.Remoting; using System.Security.Permissions; using Microsoft.Scripting.Runtime; using Microsoft.Scripting.Utils; using System.Linq.Expressions; using System.Dynamic.Binders; namespace Microsoft.Scripting.Hosting { /// /// ObjectOperations provide a large catalogue of object operations such as member access, conversions, /// indexing, and things like addition. There are several introspection and tool support services available /// for more advanced hosts. /// /// You get ObjectOperation instances from ScriptEngine, and they are bound to their engines for the semantics /// of the operations. There is a default instance of ObjectOperations you can share across all uses of the /// engine. However, very advanced hosts can create new instances. /// public sealed class ObjectOperations #if !SILVERLIGHT : MarshalByRefObject #endif { private readonly DynamicOperations _ops; private readonly ScriptEngine _engine; // friend class: DynamicOperations internal ObjectOperations(DynamicOperations ops, ScriptEngine engine) { Assert.NotNull(ops); Assert.NotNull(engine); _ops = ops; _engine = engine; } public ScriptEngine Engine { get { return _engine; } } #pragma warning disable 618 #region Local Operations /// /// Returns true if the object can be called, false if it cannot. /// /// Even if an object is callable Call may still fail if an incorrect number of arguments or type of arguments are provided. /// public bool IsCallable(object obj) { return _ops.DoOperation(StandardOperators.IsCallable, obj); } /// /// Calls the provided object with the given parameters and returns the result. /// /// The prefered way of calling objects is to convert the object to a strongly typed delegate /// using the ConvertTo methods and then invoking that delegate. /// public object Call(object obj, params object[] parameters) { return _ops.Invoke(obj, parameters); } /// /// Invokes a member on the provided object with the given parameters and returns the result. /// public object InvokeMember(object obj, string memberName, params object[] parameters) { return _ops.InvokeMember(obj, memberName, parameters); } public object CreateInstance(object obj, params object[] parameters) { return _ops.CreateInstance(obj, parameters); } /// /// Gets the member name from the object obj. Throws an exception if the member does not exist or is write-only. /// public object GetMember(object obj, string name) { return _ops.GetMember(obj, name); } /// /// Gets the member name from the object obj and converts it to the type T. Throws an exception if the /// member does not exist, is write-only, or cannot be converted. /// public T GetMember(object obj, string name) { return _ops.GetMember(obj, name); } /// /// Gets the member name from the object obj. Returns true if the member is successfully retrieved and /// stores the value in the value out param. /// public bool TryGetMember(object obj, string name, out object value) { return _ops.TryGetMember(obj, name, out value); } /// /// Returns true if the object has a member named name, false if the member does not exist. /// public bool ContainsMember(object obj, string name) { return _ops.ContainsMember(obj, name); } /// /// Removes the member name from the object obj. Returns true if the member was successfully removed /// or false if the member does not exist. /// public bool RemoveMember(object obj, string name) { return _ops.RemoveMember(obj, name); } /// /// Sets the member name on object obj to value. /// public void SetMember(object obj, string name, object value) { _ops.SetMember(obj, name, value); } /// /// Sets the member name on object obj to value. This overload can be used to avoid /// boxing and casting of strongly typed members. /// public void SetMember(object obj, string name, T value) { _ops.SetMember(obj, name, value); } /// /// Gets the member name from the object obj. Throws an exception if the member does not exist or is write-only. /// public object GetMember(object obj, string name, bool ignoreCase) { return _ops.GetMember(obj, name, ignoreCase); } /// /// Gets the member name from the object obj and converts it to the type T. Throws an exception if the /// member does not exist, is write-only, or cannot be converted. /// public T GetMember(object obj, string name, bool ignoreCase) { return _ops.GetMember(obj, name, ignoreCase); } /// /// Gets the member name from the object obj. Returns true if the member is successfully retrieved and /// stores the value in the value out param. /// public bool TryGetMember(object obj, string name, bool ignoreCase, out object value) { return _ops.TryGetMember(obj, name, ignoreCase, out value); } /// /// Returns true if the object has a member named name, false if the member does not exist. /// public bool ContainsMember(object obj, string name, bool ignoreCase) { return _ops.ContainsMember(obj, name, ignoreCase); } /// /// Removes the member name from the object obj. Returns true if the member was successfully removed /// or false if the member does not exist. /// public bool RemoveMember(object obj, string name, bool ignoreCase) { return _ops.RemoveMember(obj, name, ignoreCase); } /// /// Sets the member name on object obj to value. /// public void SetMember(object obj, string name, object value, bool ignoreCase) { _ops.SetMember(obj, name, value, ignoreCase); } /// /// Sets the member name on object obj to value. This overload can be used to avoid /// boxing and casting of strongly typed members. /// public void SetMember(object obj, string name, T value, bool ignoreCase) { _ops.SetMember(obj, name, value, ignoreCase); } /// /// Convers the object obj to the type T. /// public T ConvertTo(object obj) { return _ops.ConvertTo(obj); } /// /// Converts the object obj to the type type. /// public object ConvertTo(object obj, Type type) { ContractUtils.RequiresNotNull(type, "type"); return _ops.ConvertTo(obj, type); } /// /// Converts the object obj to the type T. Returns true if the value can be converted, false if it cannot. /// public bool TryConvertTo(object obj, out T result) { return _ops.TryConvertTo(obj, out result); } /// /// Converts the object obj to the type type. Returns true if the value can be converted, false if it cannot. /// public bool TryConvertTo(object obj, Type type, out object result) { return _ops.TryConvertTo(obj, type, out result); } /// /// Converts the object obj to the type T including explicit conversions which may lose information. /// public T ExplicitConvertTo(object obj) { return _ops.ExplicitConvertTo(obj); } /// /// Converts the object obj to the type type including explicit conversions which may lose information. /// public object ExplicitConvertTo(object obj, Type type) { ContractUtils.RequiresNotNull(type, "type"); return _ops.ExplicitConvertTo(obj, type); } /// /// Converts the object obj to the type T including explicit conversions which may lose information. /// /// Returns true if the value can be converted, false if it cannot. /// public bool TryExplicitConvertTo(object obj, out T result) { return _ops.TryExplicitConvertTo(obj, out result); } /// /// Converts the object obj to the type type including explicit conversions which may lose information. /// /// Returns true if the value can be converted, false if it cannot. /// public bool TryExplicitConvertTo(object obj, Type type, out object result) { return _ops.TryExplicitConvertTo(obj, type, out result); } /// /// Performs a generic unary operation on the specified target and returns the result. /// public object DoOperation(ExpressionType operation, object target) { return _ops.DoOperation(operation, target); } /// /// Performs a generic unary operation on the strongly typed target and returns the value as the specified type /// public TResult DoOperation(ExpressionType operation, TTarget target) { return _ops.DoOperation(operation, target); } /// /// Performs the generic binary operation on the specified targets and returns the result. /// public object DoOperation(ExpressionType operation, object target, object other) { return _ops.DoOperation(operation, target, other); } /// /// Peforms the generic binary operation on the specified strongly typed targets and returns /// the strongly typed result. /// public TResult DoOperation(ExpressionType operation, TTarget target, TOther other) { return _ops.DoOperation(operation, target, other); } /// /// Performs a generic unary operation on the specified target and returns the result. /// public object DoOperation(Operators op, object target) { return _ops.DoOperation(StandardOperators.FromOperator(op), target); } /// /// Performs a generic unary operation on the strongly typed target and returns the value as the specified type /// public TResult DoOperation(Operators op, TTarget target) { return _ops.DoOperation(StandardOperators.FromOperator(op), target); } /// /// Performs the generic binary operation on the specified targets and returns the result. /// public object DoOperation(Operators op, object target, object other) { return _ops.DoOperation(op, target, other); } /// /// Peforms the generic binary operation on the specified strongly typed targets and returns /// the strongly typed result. /// public TResult DoOperation(Operators op, TTarget target, TOther other) { return _ops.DoOperation(StandardOperators.FromOperator(op), target, other); } /// /// Performs addition on the specified targets and returns the result. Throws an exception /// if the operation cannot be performed. /// public object Add(object self, object other) { return _ops.DoOperation(Operators.Add, self, other); } /// /// Performs subtraction on the specified targets and returns the result. Throws an exception /// if the operation cannot be performed. /// public object Subtract(object self, object other) { return _ops.DoOperation(Operators.Subtract, self, other); } /// /// Raises the first object to the power of the second object. Throws an exception /// if the operation cannot be performed. /// public object Power(object self, object other) { return _ops.DoOperation(Operators.Power, self, other); } /// /// Multiplies the two objects. Throws an exception /// if the operation cannot be performed. /// public object Multiply(object self, object other) { return _ops.DoOperation(Operators.Multiply, self, other); } /// /// Divides the first object by the second object. Throws an exception /// if the operation cannot be performed. /// public object Divide(object self, object other) { return _ops.DoOperation(Operators.Divide, self, other); } /// /// Performs modulus of the 1st object by the second object. Throws an exception /// if the operation cannot be performed. /// public object Modulus(object self, object other) { return _ops.DoOperation(Operators.Mod, self, other); } /// /// Shifts the left object left by the right object. Throws an exception if the /// operation cannot be performed. /// public object LeftShift(object self, object other) { return _ops.DoOperation(Operators.LeftShift, self, other); } /// /// Shifts the left object right by the right object. Throws an exception if the /// operation cannot be performed. /// public object RightShift(object self, object other) { return _ops.DoOperation(Operators.RightShift, self, other); } /// /// Performs a bitwise-and of the two operands. Throws an exception if the operation /// cannot be performed. /// public object BitwiseAnd(object self, object other) { return _ops.DoOperation(Operators.BitwiseAnd, self, other); } /// /// Performs a bitwise-or of the two operands. Throws an exception if the operation /// cannot be performed. /// public object BitwiseOr(object self, object other) { return _ops.DoOperation(Operators.BitwiseOr, self, other); } /// /// Performs a exclusive-or of the two operands. Throws an exception if the operation /// cannot be performed. /// public object ExclusiveOr(object self, object other) { return _ops.DoOperation(Operators.ExclusiveOr, self, other); } /// /// Compares the two objects and returns true if the left object is less than the right object. /// Throws an exception if hte comparison cannot be performed. /// public bool LessThan(object self, object other) { return _ops.DoOperation(StandardOperators.LessThan, self, other); } /// /// Compares the two objects and returns true if the left object is greater than the right object. /// Throws an exception if hte comparison cannot be performed. /// public bool GreaterThan(object self, object other) { return _ops.DoOperation(StandardOperators.GreaterThan, self, other); } /// /// Compares the two objects and returns true if the left object is less than or equal to the right object. /// Throws an exception if hte comparison cannot be performed. /// public bool LessThanOrEqual(object self, object other) { return _ops.DoOperation(StandardOperators.LessThanOrEqual, self, other); } /// /// Compares the two objects and returns true if the left object is greater than or equal to the right object. /// Throws an exception if hte comparison cannot be performed. /// public bool GreaterThanOrEqual(object self, object other) { return _ops.DoOperation(StandardOperators.GreaterThanOrEqual, self, other); } /// /// Compares the two objects and returns true if the left object is equal to the right object. /// Throws an exception if the comparison cannot be performed. /// public bool Equal(object self, object other) { return _ops.DoOperation(StandardOperators.Equal, self, other); } /// /// Compares the two objects and returns true if the left object is not equal to the right object. /// Throws an exception if hte comparison cannot be performed. /// public bool NotEqual(object self, object other) { return _ops.DoOperation(StandardOperators.NotEqual, self, other); } /// /// Returns a string which describes the object as it appears in source code /// public string GetCodeRepresentation(object obj) { return obj.ToString(); //return _ops.DoOperation(StandardOperators.CodeRepresentation, obj); } /// /// Returns a list of strings which contain the known members of the object. /// public IList OldGetMemberNames(object obj) { return _ops.DoOperation>(StandardOperators.MemberNames, obj); } /// /// Returns a list of strings which contain the known members of the object. /// public IList GetMemberNames(object obj) { return _ops.GetMemberNames(obj); } /// /// Returns a string providing documentation for the specified object. /// public string GetDocumentation(object obj) { return _ops.DoOperation(StandardOperators.Documentation, obj); } /// /// Returns a list of signatures applicable for calling the specified object in a form displayable to the user. /// public IList GetCallSignatures(object obj) { return _ops.DoOperation>(StandardOperators.CallSignatures, obj); } #endregion #pragma warning restore 618 #region Remote APIs #if !SILVERLIGHT // ObjectHandle overloads // /// /// Returns true if the remote object is callable. /// public bool IsCallable(ObjectHandle obj) { return IsCallable(GetLocalObject(obj)); } /// /// Calls the specified remote object with the specified remote parameters. /// /// Though delegates are preferable for calls they may not always be usable for remote objects. /// public ObjectHandle Call(ObjectHandle obj, params ObjectHandle[] parameters) { ContractUtils.RequiresNotNull(parameters, "parameters"); return new ObjectHandle(Call(GetLocalObject(obj), GetLocalObjects(parameters))); } /// /// Calls the specified remote object with the local parameters which will be serialized /// to the remote app domain. /// public ObjectHandle Call(ObjectHandle obj, params object[] parameters) { return new ObjectHandle(Call(GetLocalObject(obj), parameters)); } public ObjectHandle Create(ObjectHandle obj, params ObjectHandle[] parameters) { return new ObjectHandle(CreateInstance(GetLocalObject(obj), GetLocalObjects(parameters))); } public ObjectHandle Create(ObjectHandle obj, params object[] parameters) { return new ObjectHandle(CreateInstance(GetLocalObject(obj), parameters)); } /// /// Sets the remote object as a member on the provided remote object. /// public void SetMember(ObjectHandle obj, string name, ObjectHandle value) { SetMember(GetLocalObject(obj), name, GetLocalObject(value)); } /// /// Sets the member name on the remote object obj to value. This overload can be used to avoid /// boxing and casting of strongly typed members. /// public void SetMember(ObjectHandle obj, string name, T value) { SetMember(GetLocalObject(obj), name, value); } /// /// Gets the member name on the remote object. Throws an exception if the member is not defined or /// is write-only. /// public ObjectHandle GetMember(ObjectHandle obj, string name) { return new ObjectHandle(GetMember(GetLocalObject(obj), name)); } /// /// Gets the member name on the remote object. Throws an exception if the member is not defined or /// is write-only. /// public T GetMember(ObjectHandle obj, string name) { return GetMember(GetLocalObject(obj), name); } /// /// Gets the member name on the remote object. Returns false if the member is not defined or /// is write-only. /// public bool TryGetMember(ObjectHandle obj, string name, out ObjectHandle value) { object val; if (TryGetMember(GetLocalObject(obj), name, out val)) { value = new ObjectHandle(val); return true; } value = null; return false; } /// /// Tests to see if the member name is defined on the remote object. /// public bool ContainsMember(ObjectHandle obj, string name) { return ContainsMember(GetLocalObject(obj), name); } /// /// Removes the member from the remote object /// public bool RemoveMember(ObjectHandle obj, string name) { return RemoveMember(GetLocalObject(obj), name); } /// /// Converts the remote object into the specified type returning a handle to /// the new remote object. /// public ObjectHandle ConvertTo(ObjectHandle obj) { return new ObjectHandle(ConvertTo(GetLocalObject(obj))); } /// /// Converts the remote object into the specified type returning a handle to /// the new remote object. /// public ObjectHandle ConvertTo(ObjectHandle obj, Type type) { return new ObjectHandle(ConvertTo(GetLocalObject(obj), type)); } /// /// Converts the remote object into the specified type returning a handle to /// the new remote object. Returns true if the value can be converted, /// false if it cannot. /// public bool TryConvertTo(ObjectHandle obj, out ObjectHandle result) { T resultObj; if (TryConvertTo(GetLocalObject(obj), out resultObj)) { result = new ObjectHandle(resultObj); return true; } result = null; return false; } /// /// Converts the remote object into the specified type returning a handle to /// the new remote object. Returns true if the value can be converted, /// false if it cannot. /// public bool TryConvertTo(ObjectHandle obj, Type type, out ObjectHandle result) { object resultObj; if (TryConvertTo(GetLocalObject(obj), type, out resultObj)) { result = new ObjectHandle(resultObj); return true; } result = null; return false; } /// /// Converts the object obj to the type T including explicit conversions which may lose information. /// public ObjectHandle ExplicitConvertTo(ObjectHandle obj) { return new ObjectHandle(_ops.ExplicitConvertTo(GetLocalObject(obj))); } /// /// Converts the object obj to the type type including explicit conversions which may lose information. /// public ObjectHandle ExplicitConvertTo(ObjectHandle obj, Type type) { ContractUtils.RequiresNotNull(type, "type"); return new ObjectHandle(_ops.ExplicitConvertTo(GetLocalObject(obj), type)); } /// /// Converts the object obj to the type T including explicit conversions which may lose information. /// /// Returns true if the value can be converted, false if it cannot. /// public bool TryExplicitConvertTo(ObjectHandle obj, out ObjectHandle result) { T outp; bool res = _ops.TryExplicitConvertTo(GetLocalObject(obj), out outp); if (res) { result = new ObjectHandle(obj); } else { result = null; } return res; } /// /// Converts the object obj to the type type including explicit conversions which may lose information. /// /// Returns true if the value can be converted, false if it cannot. /// public bool TryExplicitConvertTo(ObjectHandle obj, Type type, out ObjectHandle result) { object outp; bool res = _ops.TryExplicitConvertTo(GetLocalObject(obj), type, out outp); if (res) { result = new ObjectHandle(obj); } else { result = null; } return res; } /// /// Unwraps the remote object and converts it into the specified type before /// returning it. /// public T Unwrap(ObjectHandle obj) { return ConvertTo(GetLocalObject(obj)); } /// /// Performs the specified unary operator on the remote object. /// public object DoOperation(Operators op, ObjectHandle target) { return DoOperation(op, GetLocalObject(target)); } /// /// Performs the specified binary operator on the remote object. /// public ObjectHandle DoOperation(Operators op, ObjectHandle target, ObjectHandle other) { return new ObjectHandle(DoOperation(op, GetLocalObject(target), GetLocalObject(other))); } /// /// Adds the two remote objects. Throws an exception if the operation cannot be performed. /// public ObjectHandle Add(ObjectHandle self, ObjectHandle other) { return new ObjectHandle(Add(GetLocalObject(self), GetLocalObject(other))); } /// /// Subtracts the 1st remote object from the second. Throws an exception if the operation cannot be performed. /// public ObjectHandle Subtract(ObjectHandle self, ObjectHandle other) { return new ObjectHandle(Subtract(GetLocalObject(self), GetLocalObject(other))); } /// /// Raises the 1st remote object to the power of the 2nd. Throws an exception if the operation cannot be performed. /// public ObjectHandle Power(ObjectHandle self, ObjectHandle other) { return new ObjectHandle(Power(GetLocalObject(self), GetLocalObject(other))); } /// /// Multiplies the two remote objects. Throws an exception if the operation cannot be performed. /// public ObjectHandle Multiply(ObjectHandle self, ObjectHandle other) { return new ObjectHandle(Multiply(GetLocalObject(self), GetLocalObject(other))); } /// /// Divides the 1st remote object by the 2nd. Throws an exception if the operation cannot be performed. /// public ObjectHandle Divide(ObjectHandle self, ObjectHandle other) { return new ObjectHandle(Divide(GetLocalObject(self), GetLocalObject(other))); } /// /// Performs modulus on the 1st remote object by the 2nd. Throws an exception if the operation cannot be performed. /// public ObjectHandle Modulus(ObjectHandle self, ObjectHandle other) { return new ObjectHandle(Modulus(GetLocalObject(self), GetLocalObject(other))); } /// /// Shifts the 1st remote object left by the 2nd remote object. Throws an exception if the operation cannot be performed. /// public ObjectHandle LeftShift(ObjectHandle self, ObjectHandle other) { return new ObjectHandle(LeftShift(GetLocalObject(self), GetLocalObject(other))); } /// /// Shifts the 1st remote object right by the 2nd remote object. Throws an exception if the operation cannot be performed. /// public ObjectHandle RightShift(ObjectHandle self, ObjectHandle other) { return new ObjectHandle(RightShift(GetLocalObject(self), GetLocalObject(other))); } /// /// Performs bitwise-and on the two remote objects. Throws an exception if the operation cannot be performed. /// public ObjectHandle BitwiseAnd(ObjectHandle self, ObjectHandle other) { return new ObjectHandle(BitwiseAnd(GetLocalObject(self), GetLocalObject(other))); } /// /// Performs bitwise-or on the two remote objects. Throws an exception if the operation cannot be performed. /// public ObjectHandle BitwiseOr(ObjectHandle self, ObjectHandle other) { return new ObjectHandle(BitwiseOr(GetLocalObject(self), GetLocalObject(other))); } /// /// Performs exclusive-or on the two remote objects. Throws an exception if the operation cannot be performed. /// public ObjectHandle ExclusiveOr(ObjectHandle self, ObjectHandle other) { return new ObjectHandle(ExclusiveOr(GetLocalObject(self), GetLocalObject(other))); } /// /// Compares the two remote objects and returns true if the 1st is less than the 2nd. Throws an exception if the operation cannot be performed. /// public bool LessThan(ObjectHandle self, ObjectHandle other) { return LessThan(GetLocalObject(self), GetLocalObject(other)); } /// /// Compares the two remote objects and returns true if the 1st is greater than the 2nd. Throws an exception if the operation cannot be performed. /// public bool GreaterThan(ObjectHandle self, ObjectHandle other) { return GreaterThan(GetLocalObject(self), GetLocalObject(other)); } /// /// Compares the two remote objects and returns true if the 1st is less than or equal to the 2nd. Throws an exception if the operation cannot be performed. /// public bool LessThanOrEqual(ObjectHandle self, ObjectHandle other) { return LessThanOrEqual(GetLocalObject(self), GetLocalObject(other)); } /// /// Compares the two remote objects and returns true if the 1st is greater than or equal to than the 2nd. Throws an exception if the operation cannot be performed. /// public bool GreaterThanOrEqual(ObjectHandle self, ObjectHandle other) { return GreaterThanOrEqual(GetLocalObject(self), GetLocalObject(other)); } /// /// Compares the two remote objects and returns true if the 1st is equal to the 2nd. Throws an exception if the operation cannot be performed. /// public bool Equal(ObjectHandle self, ObjectHandle other) { return Equal(GetLocalObject(self), GetLocalObject(other)); } /// /// Compares the two remote objects and returns true if the 1st is not equal to the 2nd. Throws an exception if the operation cannot be performed. /// public bool NotEqual(ObjectHandle self, ObjectHandle other) { return NotEqual(GetLocalObject(self), GetLocalObject(other)); } /// /// Returns a string which describes the remote object as it appears in source code /// public string GetCodeRepresentation(ObjectHandle obj) { return GetCodeRepresentation(GetLocalObject(obj)); } /// /// Returns a list of strings which contain the known members of the remote object. /// public IList GetMemberNames(ObjectHandle obj) { return OldGetMemberNames(GetLocalObject(obj)); } /// /// Returns a string providing documentation for the specified remote object. /// public string GetDocumentation(ObjectHandle obj) { return GetDocumentation(GetLocalObject(obj)); } /// /// Returns a list of signatures applicable for calling the specified object in a form displayable to the user. /// public IList GetCallSignatures(ObjectHandle obj) { return GetCallSignatures(GetLocalObject(obj)); } /// /// Helper to unwrap an object - in the future maybe we should validate the current app domain. /// private static object GetLocalObject(ObjectHandle obj) { ContractUtils.RequiresNotNull(obj, "obj"); return obj.Unwrap(); } /// /// Helper to unwrap multiple objects /// private static object[] GetLocalObjects(ObjectHandle[] ohs) { Debug.Assert(ohs != null); object[] res = new object[ohs.Length]; for (int i = 0; i < res.Length; i++) { res[i] = GetLocalObject(ohs[i]); } return res; } #endif #endregion #if !SILVERLIGHT // TODO: Figure out what is the right lifetime [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)] public override object InitializeLifetimeService() { return null; } #endif } }