diff --git a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptJsEngineBase.cs b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptJsEngineBase.cs
index 5cf3ec6..ee02f2e 100644
--- a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptJsEngineBase.cs
+++ b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptJsEngineBase.cs
@@ -1,11 +1,15 @@
namespace MsieJavaScriptEngine.ActiveScript
{
using System;
+ using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
+ using System.Runtime.InteropServices.Expando;
using System.Windows.Threading;
+ using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO;
+
using Constants;
using Helpers;
using Resources;
@@ -14,7 +18,7 @@
///
/// Base class of the ActiveScript JavaScript engine
///
- internal abstract class ActiveScriptJsEngineBase : IInnerJsEngine
+ internal abstract class ActiveScriptJsEngineBase : IInnerJsEngine, IActiveScriptSite
{
///
/// Name of resource, which contains a ECMAScript 5 Polyfill
@@ -37,9 +41,29 @@ internal abstract class ActiveScriptJsEngineBase : IInnerJsEngine
private IActiveScript _activeScript;
///
- /// Instance of site for the ActiveScript engine
+ /// Instance of ActiveScriptParseWrapper
+ ///
+ private IActiveScriptParseWrapper _activeScriptParse;
+
+ ///
+ /// Instance of script dispatch
+ ///
+ private IExpando _dispatch;
+
+ ///
+ /// List of host items
///
- private ActiveScriptSiteWrapper _activeScriptSite;
+ private Dictionary _hostItems = new Dictionary();
+
+ ///
+ /// Host-defined document version string
+ ///
+ private readonly string _documentVersion;
+
+ ///
+ /// Last ActiveScript exception
+ ///
+ private ActiveScriptException _lastException;
///
/// JavaScript engine mode
@@ -63,7 +87,7 @@ internal abstract class ActiveScriptJsEngineBase : IInnerJsEngine
///
- /// Constructs instance of the ActiveScript JavaScript engine
+ /// Constructs an instance of the ActiveScript JavaScript engine
///
/// CLSID of JavaScript engine
/// JavaScript engine mode
@@ -95,10 +119,10 @@ protected ActiveScriptJsEngineBase(string clsid, JsEngineMode engineMode, string
var activeScriptProperty = _activeScript as IActiveScriptProperty;
if (activeScriptProperty != null)
{
- object scriptLanguageVersion = (int) languageVersion;
- uint result = activeScriptProperty.SetProperty((uint) ScriptProperty.InvokeVersioning,
+ object scriptLanguageVersion = (int)languageVersion;
+ uint result = activeScriptProperty.SetProperty((uint)ScriptProperty.InvokeVersioning,
IntPtr.Zero, ref scriptLanguageVersion);
- if (result != (uint) ScriptHResult.Ok)
+ if (result != (uint)ScriptHResult.Ok)
{
throw new JsEngineLoadException(
string.Format(Strings.Runtime_ActiveScriptLanguageVersionSelectionFailed, languageVersion));
@@ -106,7 +130,14 @@ protected ActiveScriptJsEngineBase(string clsid, JsEngineMode engineMode, string
}
}
- _activeScriptSite = new ActiveScriptSiteWrapper(_pActiveScript, _activeScript);
+ _activeScriptParse = new ActiveScriptParseWrapper(_pActiveScript, _activeScript);
+ _activeScriptParse.InitNew();
+
+ _activeScript.SetScriptSite(this);
+ _activeScript.SetScriptState(ScriptState.Started);
+
+ InitScriptDispatch();
+ _documentVersion = DateTime.UtcNow.ToString("o");
LoadResources(useEcmaScript5Polyfill, useJson2Library);
}
@@ -203,7 +234,7 @@ private object MapToHostType(object value)
}
///
- /// Makes a mapping of array itemp from the script type to a host type
+ /// Makes a mapping of array items from the script type to a host type
///
/// The source array
/// The mapped array
@@ -229,6 +260,49 @@ private JsRuntimeException ConvertActiveScriptExceptionToJsRuntimeException(
return jsEngineException;
}
+ ///
+ /// Initializes a script dispatch
+ ///
+ private void InitScriptDispatch()
+ {
+ IExpando dispatch = null;
+ object obj;
+
+ _activeScript.GetScriptDispatch(null, out obj);
+
+ if (obj != null && obj.GetType().IsCOMObject)
+ {
+ dispatch = obj as IExpando;
+ }
+
+ if (dispatch == null)
+ {
+ throw new InvalidOperationException(Strings.Runtime_ActiveScriptDispatcherNotInitialized);
+ }
+
+ _dispatch = dispatch;
+ }
+
+ ///
+ /// Gets and resets a last exception. Returns null for none.
+ ///
+ private ActiveScriptException GetAndResetLastException()
+ {
+ ActiveScriptException temp = _lastException;
+ _lastException = null;
+
+ return temp;
+ }
+
+ private void ThrowError()
+ {
+ ActiveScriptException last = GetAndResetLastException();
+ if (last != null)
+ {
+ throw last;
+ }
+ }
+
private void InvokeScript(Action action)
{
try
@@ -273,6 +347,139 @@ private T InvokeScript(Func func)
}
}
+ ///
+ /// Executes a script text
+ ///
+ /// Script text
+ /// Flag that script text needs to run as an expression
+ /// Result of the execution
+ private object InnerExecute(string code, bool isExpression)
+ {
+ object result;
+
+ try
+ {
+ result = _activeScriptParse.ParseScriptText(code, null, null, null, IntPtr.Zero,
+ 0, isExpression ? ScriptTextFlags.IsExpression : ScriptTextFlags.IsVisible);
+ }
+ catch
+ {
+ ThrowError();
+ throw;
+ }
+
+ // Check for parse error
+ ThrowError();
+
+ return result;
+ }
+
+ ///
+ /// Calls a function
+ ///
+ /// Function name
+ /// Function arguments
+ /// Result of the function execution
+ private object InnerCallFunction(string functionName, params object[] args)
+ {
+ object result;
+
+ try
+ {
+ result = _dispatch.InvokeMember(functionName, BindingFlags.InvokeMethod,
+ null, _dispatch, args, null, CultureInfo.InvariantCulture, null);
+ }
+ catch
+ {
+ ThrowError();
+ throw;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Gets a value of variable
+ ///
+ /// Name of variable
+ /// Value of variable
+ private object InnerGetVariableValue(string variableName)
+ {
+ object variableValue;
+
+ try
+ {
+ variableValue = _dispatch.InvokeMember(variableName, BindingFlags.GetProperty,
+ null, _dispatch, new object[0], null,
+ CultureInfo.InvariantCulture, null);
+ }
+ catch
+ {
+ ThrowError();
+ throw;
+ }
+
+ return variableValue;
+ }
+
+ ///
+ /// Sets a value to variable
+ ///
+ /// Name of variable
+ /// Value of variable
+ private void InnerSetVariableValue(string variableName, object value)
+ {
+ object[] args = { value };
+
+ try
+ {
+ _dispatch.InvokeMember(variableName, BindingFlags.SetProperty, null, _dispatch,
+ args, null, CultureInfo.InvariantCulture, null);
+ }
+ catch (MissingMemberException)
+ {
+ _dispatch.AddProperty(variableName);
+ _dispatch.InvokeMember(variableName, BindingFlags.SetProperty, null, _dispatch,
+ args, null, CultureInfo.InvariantCulture, null);
+ }
+ catch
+ {
+ ThrowError();
+ throw;
+ }
+ }
+
+ private void EmbedHostItem(string itemName, object value)
+ {
+ InvokeScript(() =>
+ {
+ object oldValue = null;
+ if (_hostItems.ContainsKey(itemName))
+ {
+ oldValue = _hostItems[itemName];
+ }
+ _hostItems[itemName] = value;
+
+ try
+ {
+ _activeScript.AddNamedItem(itemName, ScriptItemFlags.IsVisible | ScriptItemFlags.GlobalMembers);
+ }
+ catch (Exception)
+ {
+ if (oldValue != null)
+ {
+ _hostItems[itemName] = oldValue;
+ }
+ else
+ {
+ _hostItems.Remove(itemName);
+ }
+
+ throw;
+ }
+ });
+ }
+
///
/// Loads a resources
///
@@ -323,31 +530,156 @@ private void ExecuteResource(string resourceName, Type type)
/// managed objects contained in fields of class
private void Dispose(bool disposing)
{
- _dispatcher.Invoke(DispatcherPriority.Input, (Action)InnerDispose);
+ _dispatcher.Invoke(DispatcherPriority.Input, (Action)(() =>
+ {
+ if (!_disposed)
+ {
+ _disposed = true;
+
+ if (_dispatch != null)
+ {
+ ComHelpers.ReleaseComObject(ref _dispatch, !disposing);
+ _dispatch = null;
+ }
+
+ if (_activeScriptParse != null)
+ {
+ _activeScriptParse.Dispose();
+ _activeScriptParse = null;
+ }
+
+ if (_activeScript != null)
+ {
+ _activeScript.Close();
+ _activeScript = null;
+ }
+
+ ComHelpers.ReleaseAndEmpty(ref _pActiveScript);
+
+ if (_hostItems != null)
+ {
+ _hostItems.Clear();
+ _hostItems = null;
+ }
+
+ _lastException = null;
+ }
+ }));
+ }
+
+ #region IActiveScriptSite implementation
+
+ ///
+ /// Retrieves the locale identifier associated with the host's user interface. The scripting
+ /// engine uses the identifier to ensure that error strings and other user-interface elements
+ /// generated by the engine appear in the appropriate language.
+ ///
+ /// A variable that receives the locale identifier for user-interface
+ /// elements displayed by the scripting engine
+ void IActiveScriptSite.GetLcid(out int lcid)
+ {
+ lcid = CultureInfo.CurrentCulture.LCID;
}
- private void InnerDispose()
+ ///
+ /// Allows the scripting engine to obtain information about an item added with the
+ /// IActiveScript.AddNamedItem method
+ ///
+ /// The name associated with the item, as specified in the
+ /// IActiveScript.AddNamedItem method
+ /// A bit mask specifying what information about the item should be
+ /// returned. The scripting engine should request the minimum amount of information possible
+ /// because some of the return parameters (for example, ITypeInfo) can take considerable
+ /// time to load or generate
+ /// A variable that receives a pointer to the IUnknown interface associated
+ /// with the given item. The scripting engine can use the IUnknown.QueryInterface method to
+ /// obtain the IDispatch interface for the item. This parameter receives null if mask
+ /// does not include the ScriptInfo.IUnknown value. Also, it receives null if there is no
+ /// object associated with the item name; this mechanism is used to create a simple class when
+ /// the named item was added with the ScriptItem.CodeOnly flag set in the
+ /// IActiveScript.AddNamedItem method.
+ /// A variable that receives a pointer to the ITypeInfo interface
+ /// associated with the item. This parameter receives null if mask does not include the
+ /// ScriptInfo.ITypeInfo value, or if type information is not available for this item. If type
+ /// information is not available, the object cannot source events, and name binding must be
+ /// realized with the IDispatch.GetIDsOfNames method. Note that the ITypeInfo interface
+ /// retrieved describes the item's coclass (TKIND_COCLASS) because the object may support
+ /// multiple interfaces and event interfaces. If the item supports the IProvideMultipleTypeInfo
+ /// interface, the ITypeInfo interface retrieved is the same as the index zero ITypeInfo that
+ /// would be obtained using the IProvideMultipleTypeInfo.GetInfoOfIndex method.
+ void IActiveScriptSite.GetItemInfo(string name, ScriptInfoFlags mask, ref IntPtr pUnkItem, ref IntPtr pTypeInfo)
{
- if (!_disposed)
+ object item = _hostItems[name];
+ if (item == null)
{
- _disposed = true;
-
- if (_activeScriptSite != null)
- {
- _activeScriptSite.Dispose();
- _activeScriptSite = null;
- }
+ throw new COMException(
+ string.Format(Strings.Runtime_ItemNotFound, name), ComErrorCode.ElementNotFound);
+ }
- if (_activeScript != null)
- {
- _activeScript.Close();
- _activeScript = null;
- }
+ if (mask.HasFlag(ScriptInfoFlags.IUnknown))
+ {
+ pUnkItem = Marshal.GetIDispatchForObject(item);
+ }
- ComHelpers.ReleaseAndEmpty(ref _pActiveScript);
+ if (mask.HasFlag(ScriptInfoFlags.ITypeInfo))
+ {
+ pTypeInfo = Marshal.GetITypeInfoForType(item.GetType());
}
}
+ ///
+ /// Retrieves a host-defined string that uniquely identifies the current document version. If
+ /// the related document has changed outside the scope of Windows Script (as in the case of an
+ /// HTML page being edited with Notepad), the scripting engine can save this along with its
+ /// persisted state, forcing a recompile the next time the script is loaded.
+ ///
+ /// The host-defined document version string
+ void IActiveScriptSite.GetDocVersionString(out string version)
+ {
+ version = _documentVersion;
+ }
+
+ ///
+ /// Informs the host that the script has completed execution
+ ///
+ /// A variable that contains the script result, or null if the script
+ /// produced no result
+ /// Contains exception information generated when the script
+ /// terminated, or null if no exception was generated
+ void IActiveScriptSite.OnScriptTerminate(object result, EXCEPINFO exceptionInfo)
+ { }
+
+ ///
+ /// Informs the host that the scripting engine has changed states
+ ///
+ /// Indicates the new script state
+ void IActiveScriptSite.OnStateChange(ScriptState scriptState)
+ { }
+
+ ///
+ /// Informs the host that an execution error occurred while the engine was running the script.
+ ///
+ /// A host can use this interface to obtain information about the
+ /// execution error
+ void IActiveScriptSite.OnScriptError(IActiveScriptError scriptError)
+ {
+ _lastException = ActiveScriptException.Create(scriptError);
+ }
+
+ ///
+ /// Informs the host that the scripting engine has begun executing the script code
+ ///
+ void IActiveScriptSite.OnEnterScript()
+ { }
+
+ ///
+ /// Informs the host that the scripting engine has returned from executing script code
+ ///
+ void IActiveScriptSite.OnLeaveScript()
+ { }
+
+ #endregion
+
#region IInnerJsEngine implementation
public string Mode
@@ -357,7 +689,7 @@ public string Mode
public object Evaluate(string expression)
{
- object result = InvokeScript(() => _activeScriptSite.ExecuteScriptText(expression, true));
+ object result = InvokeScript(() => InnerExecute(expression, true));
result = MapToHostType(result);
return result;
@@ -367,19 +699,19 @@ public void Execute(string code)
{
InvokeScript(() =>
{
- _activeScriptSite.ExecuteScriptText(code, false);
+ InnerExecute(code, false);
});
}
public object CallFunction(string functionName, params object[] args)
{
- var processedArgs = MapToScriptType(args);
+ object[] processedArgs = MapToScriptType(args);
object result = InvokeScript(() =>
{
try
{
- return _activeScriptSite.CallFunction(functionName, processedArgs);
+ return InnerCallFunction(functionName, processedArgs);
}
catch (MissingMemberException)
{
@@ -395,18 +727,33 @@ public object CallFunction(string functionName, params object[] args)
public bool HasVariable(string variableName)
{
- var variableExist = InvokeScript(() => _activeScriptSite.HasProperty(variableName));
+ bool result = InvokeScript(() =>
+ {
+ bool variableExist;
- return variableExist;
+ try
+ {
+ object variableValue = InnerGetVariableValue(variableName);
+ variableExist = variableValue != null;
+ }
+ catch (MissingMemberException)
+ {
+ variableExist = false;
+ }
+
+ return variableExist;
+ });
+
+ return result;
}
public object GetVariableValue(string variableName)
{
- object variableValue = InvokeScript(() =>
+ object result = InvokeScript(() =>
{
try
{
- return _activeScriptSite.GetProperty(variableName);
+ return InnerGetVariableValue(variableName);
}
catch (MissingMemberException)
{
@@ -415,7 +762,7 @@ public object GetVariableValue(string variableName)
}
});
- object result = MapToHostType(variableValue);
+ result = MapToHostType(result);
return result;
}
@@ -423,43 +770,34 @@ public object GetVariableValue(string variableName)
public void SetVariableValue(string variableName, object value)
{
object processedValue = MapToScriptType(value);
-
- InvokeScript(() => _activeScriptSite.SetProperty(variableName, processedValue));
+ InvokeScript(() => InnerSetVariableValue(variableName, processedValue));
}
public void RemoveVariable(string variableName)
- {
- InvokeScript(() => _activeScriptSite.DeleteProperty(variableName));
- }
-
- public void EmbedHostObject(string itemName, object value)
{
InvokeScript(() =>
{
- var processedValue = MapToScriptType(value);
- object oldValue = _activeScriptSite.GetItem(itemName);
- _activeScriptSite.SetItem(itemName, processedValue);
+ InnerSetVariableValue(variableName, null);
- try
+ if (_hostItems.ContainsKey(variableName))
{
- _activeScript.AddNamedItem(itemName, ScriptItemFlags.IsVisible | ScriptItemFlags.GlobalMembers);
- }
- catch (Exception)
- {
- if (oldValue != null)
- {
- _activeScriptSite.SetItem(itemName, oldValue);
- }
- else
- {
- _activeScriptSite.RemoveItem(itemName);
- }
-
- throw;
+ _hostItems.Remove(variableName);
}
});
}
+ public void EmbedHostObject(string itemName, object value)
+ {
+ object processedValue = MapToScriptType(value);
+ EmbedHostItem(itemName, processedValue);
+ }
+
+ public void EmbedHostType(string itemName, Type type)
+ {
+ var typeValue = new HostType(type, _engineMode);
+ EmbedHostItem(itemName, typeValue);
+ }
+
#endregion
#region IDisposable implementation
diff --git a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptSiteWrapper.cs b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptSiteWrapper.cs
deleted file mode 100644
index cb2e430..0000000
--- a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptSiteWrapper.cs
+++ /dev/null
@@ -1,494 +0,0 @@
-namespace MsieJavaScriptEngine.ActiveScript
-{
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Reflection;
- using System.Runtime.InteropServices;
- using System.Runtime.InteropServices.Expando;
-
- using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO;
-
- using Constants;
- using Helpers;
- using Resources;
-
- internal class ActiveScriptSiteWrapper : IActiveScriptSite, IDisposable
- {
- ///
- /// Instance of native JavaScript engine
- ///
- private IActiveScript _activeScript;
-
- ///
- /// Instance of ActiveScriptParseWrapper
- ///
- private IActiveScriptParseWrapper _activeScriptParse;
-
- ///
- /// Instance of script dispatch
- ///
- private IExpando _dispatch;
-
- ///
- /// List of site items
- ///
- private Dictionary _siteItems = new Dictionary();
-
- ///
- /// Synchronizer
- ///
- private readonly object _synchronizer = new object();
-
- ///
- /// Last ActiveScript exception
- ///
- private ActiveScriptException _lastException;
-
- ///
- /// Flag that object is destroyed
- ///
- private bool _disposed;
-
- ///
- /// Gets or sets a host-defined document version string
- ///
- public string DocumentVersion
- {
- get;
- protected set;
- }
-
-
- ///
- /// Constructs instance of
- ///
- /// Pointer to an instance of native JavaScript engine
- /// Instance of native JavaScript engine
- public ActiveScriptSiteWrapper(IntPtr pActiveScript, IActiveScript activeScript)
- : this(pActiveScript, activeScript, DateTime.UtcNow.ToString("o"))
- { }
-
- ///
- /// Constructs instance of
- ///
- /// Pointer to an instance of native JavaScript engine
- /// Instance of native JavaScript engine
- /// Host-defined document version string
- public ActiveScriptSiteWrapper(IntPtr pActiveScript, IActiveScript activeScript,
- string documentVersion)
- {
- _activeScript = activeScript;
-
- _activeScriptParse = new ActiveScriptParseWrapper(pActiveScript, _activeScript);
- _activeScriptParse.InitNew();
-
- _activeScript.SetScriptSite(this);
- _activeScript.SetScriptState(ScriptState.Started);
-
- InitScriptDispatch();
-
- DocumentVersion = documentVersion;
- }
-
- ///
- /// Destructs instance of
- ///
- ~ActiveScriptSiteWrapper()
- {
- Dispose(false);
- }
-
-
- ///
- /// Initializes a script dispatch
- ///
- private void InitScriptDispatch()
- {
- IExpando dispatch = null;
- object obj;
-
- _activeScript.GetScriptDispatch(null, out obj);
-
- if (obj != null && obj.GetType().IsCOMObject)
- {
- dispatch = obj as IExpando;
- }
-
- if (dispatch == null)
- {
- throw new InvalidOperationException(Strings.Runtime_ActiveScriptDispatcherNotInitialized);
- }
-
- _dispatch = dispatch;
- }
-
- ///
- /// Allows the scripting engine to obtain information about an item added with the
- /// IActiveScript.AddNamedItem method
- ///
- /// The name associated with the item, as specified in the
- /// IActiveScript.AddNamedItem method
- public object GetItem(string name)
- {
- lock (_synchronizer)
- {
- object result;
-
- return _siteItems.TryGetValue(name, out result) ? result : null;
- }
- }
-
- public void SetItem(string name, object value)
- {
- lock (_synchronizer)
- {
- _siteItems[name] = value;
- }
- }
-
- public void RemoveItem(string name)
- {
- lock (_synchronizer)
- {
- _siteItems.Remove(name);
- }
- }
-
- ///
- /// Gets and resets a last exception. Returns null for none.
- ///
- private ActiveScriptException GetAndResetLastException()
- {
- var temp = _lastException;
- _lastException = null;
-
- return temp;
- }
-
- private void ThrowError()
- {
- var last = GetAndResetLastException();
- if (last != null)
- {
- throw last;
- }
- }
-
- ///
- /// Executes a script text
- ///
- /// Script text
- /// Flag that script text needs to run as an expression
- /// Result of the execution
- public object ExecuteScriptText(string code, bool isExpression)
- {
- object result;
-
- try
- {
- result = _activeScriptParse.ParseScriptText(code, null, null, null, IntPtr.Zero,
- 0, isExpression ? ScriptTextFlags.IsExpression : ScriptTextFlags.IsVisible);
- }
- catch
- {
- ThrowError();
- throw;
- }
-
- // Check for parse error
- ThrowError();
-
- return result;
- }
-
- ///
- /// Calls a function
- ///
- /// Function name
- /// Function arguments
- /// Result of the function execution
- public object CallFunction(string functionName, params object[] args)
- {
- object result;
- try
- {
- result = _dispatch.InvokeMember(functionName, BindingFlags.InvokeMethod,
- null, _dispatch, args, null, CultureInfo.InvariantCulture, null);
- }
- catch
- {
- ThrowError();
- throw;
- }
-
- return result;
- }
-
- ///
- /// Сhecks for the existence of a global object property
- ///
- /// Name of property
- /// Result of check (true - exists; false - not exists)
- public bool HasProperty(string propertyName)
- {
- bool propertyExist;
-
- try
- {
- object propertyValue = GetProperty(propertyName);
- propertyExist = (propertyValue != null);
- }
- catch (MissingMemberException)
- {
- propertyExist = false;
- }
- catch
- {
- ThrowError();
- throw;
- }
-
- return propertyExist;
- }
-
- ///
- /// Gets a value of global object property
- ///
- /// Name of property
- /// Value of property
- public object GetProperty(string propertyName)
- {
- object propertyValue;
-
- try
- {
- propertyValue = _dispatch.InvokeMember(propertyName, BindingFlags.GetProperty,
- null, _dispatch, new object[0], null,
- CultureInfo.InvariantCulture, null);
- }
- catch
- {
- ThrowError();
- throw;
- }
-
- return propertyValue;
- }
-
- ///
- /// Sets a value to global object property
- ///
- /// Name of property
- /// Value of property
- public void SetProperty(string propertyName, object value)
- {
- var marshaledArgs = new[] { value };
- try
- {
- _dispatch.InvokeMember(propertyName, BindingFlags.SetProperty, null, _dispatch,
- marshaledArgs, null, CultureInfo.InvariantCulture, null);
- }
- catch (MissingMemberException)
- {
- _dispatch.AddProperty(propertyName);
- _dispatch.InvokeMember(propertyName, BindingFlags.SetProperty, null, _dispatch,
- marshaledArgs, null, CultureInfo.InvariantCulture, null);
- }
- catch
- {
- ThrowError();
- throw;
- }
- }
-
- ///
- /// Removes a global object property
- ///
- /// Name of property
- public void DeleteProperty(string propertyName)
- {
- try
- {
- SetProperty(propertyName, null);
- }
- catch
- {
- ThrowError();
- throw;
- }
- }
-
- ///
- /// Destroys object
- ///
- /// Flag, allowing destruction of
- /// managed objects contained in fields of class
- private void Dispose(bool disposing)
- {
- if (!_disposed)
- {
- _disposed = true;
-
- _lastException = null;
-
- lock (_synchronizer)
- {
- if (_siteItems != null)
- {
- _siteItems.Clear();
- _siteItems = null;
- }
- }
-
- if (_dispatch != null)
- {
- ComHelpers.ReleaseComObject(ref _dispatch, !disposing);
- _dispatch = null;
- }
-
- if (_activeScriptParse != null)
- {
- _activeScriptParse.Dispose();
- _activeScriptParse = null;
- }
-
- _activeScript = null;
- }
- }
-
- #region IActiveScriptSite implementation
-
- ///
- /// Retrieves the locale identifier associated with the host's user interface. The scripting
- /// engine uses the identifier to ensure that error strings and other user-interface elements
- /// generated by the engine appear in the appropriate language.
- ///
- /// A variable that receives the locale identifier for user-interface
- /// elements displayed by the scripting engine
- public void GetLcid(out int lcid)
- {
- lcid = CultureInfo.CurrentCulture.LCID;
- }
-
- ///
- /// Allows the scripting engine to obtain information about an item added with the
- /// IActiveScript.AddNamedItem method
- ///
- /// The name associated with the item, as specified in the
- /// IActiveScript.AddNamedItem method
- /// A bit mask specifying what information about the item should be
- /// returned. The scripting engine should request the minimum amount of information possible
- /// because some of the return parameters (for example, ITypeInfo) can take considerable
- /// time to load or generate
- /// A variable that receives a pointer to the IUnknown interface associated
- /// with the given item. The scripting engine can use the IUnknown.QueryInterface method to
- /// obtain the IDispatch interface for the item. This parameter receives null if mask
- /// does not include the ScriptInfo.IUnknown value. Also, it receives null if there is no
- /// object associated with the item name; this mechanism is used to create a simple class when
- /// the named item was added with the ScriptItem.CodeOnly flag set in the
- /// IActiveScript.AddNamedItem method.
- /// A variable that receives a pointer to the ITypeInfo interface
- /// associated with the item. This parameter receives null if mask does not include the
- /// ScriptInfo.ITypeInfo value, or if type information is not available for this item. If type
- /// information is not available, the object cannot source events, and name binding must be
- /// realized with the IDispatch.GetIDsOfNames method. Note that the ITypeInfo interface
- /// retrieved describes the item's coclass (TKIND_COCLASS) because the object may support
- /// multiple interfaces and event interfaces. If the item supports the IProvideMultipleTypeInfo
- /// interface, the ITypeInfo interface retrieved is the same as the index zero ITypeInfo that
- /// would be obtained using the IProvideMultipleTypeInfo.GetInfoOfIndex method.
- public void GetItemInfo(string name, ScriptInfoFlags mask, ref IntPtr pUnkItem, ref IntPtr pTypeInfo)
- {
- object item = GetItem(name);
- if (item == null)
- {
- throw new COMException(
- string.Format(Strings.Runtime_ItemNotFound, name), ComErrorCode.ElementNotFound);
- }
-
- if (mask.HasFlag(ScriptInfoFlags.IUnknown))
- {
- pUnkItem = Marshal.GetIDispatchForObject(item);
- }
-
- if (mask.HasFlag(ScriptInfoFlags.ITypeInfo))
- {
- pTypeInfo = Marshal.GetITypeInfoForType(item.GetType());
- }
- }
-
- ///
- /// Retrieves a host-defined string that uniquely identifies the current document version. If
- /// the related document has changed outside the scope of Windows Script (as in the case of an
- /// HTML page being edited with Notepad), the scripting engine can save this along with its
- /// persisted state, forcing a recompile the next time the script is loaded.
- ///
- /// The host-defined document version string
- public void GetDocVersionString(out string version)
- {
- version = DocumentVersion;
- }
-
- ///
- /// Informs the host that the script has completed execution
- ///
- /// A variable that contains the script result, or null if the script
- /// produced no result
- /// Contains exception information generated when the script
- /// terminated, or null if no exception was generated
- public virtual void OnScriptTerminate(object result, EXCEPINFO exceptionInfo)
- { }
-
- ///
- /// Informs the host that the scripting engine has changed states
- ///
- /// Indicates the new script state
- public virtual void OnStateChange(ScriptState scriptState)
- { }
-
- ///
- /// Informs the host that an execution error occurred while the engine was running the script.
- ///
- /// A host can use this interface to obtain information about the
- /// execution error
- public void OnScriptError(IActiveScriptError scriptError)
- {
- _lastException = ActiveScriptException.Create(scriptError);
- OnScriptError(_lastException);
- }
-
- ///
- /// Informs the host that an execution error occurred while the engine was running the script
- ///
- /// The exception
- protected virtual void OnScriptError(ActiveScriptException exception)
- { }
-
- ///
- /// Informs the host that the scripting engine has begun executing the script code
- ///
- public virtual void OnEnterScript()
- { }
-
- ///
- /// Informs the host that the scripting engine has returned from executing script code
- ///
- public virtual void OnLeaveScript()
- { }
-
- #endregion
-
- #region IDisposable implementation
-
- ///
- /// Destroys object
- ///
- public void Dispose()
- {
- Dispose(true /* disposing */);
- GC.SuppressFinalize(this);
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/MsieJavaScriptEngine/Constants/SpecialMemberName.cs b/src/MsieJavaScriptEngine/Constants/SpecialMemberName.cs
new file mode 100644
index 0000000..5415c70
--- /dev/null
+++ b/src/MsieJavaScriptEngine/Constants/SpecialMemberName.cs
@@ -0,0 +1,10 @@
+namespace MsieJavaScriptEngine.Constants
+{
+ ///
+ /// Special member names
+ ///
+ internal static class SpecialMemberName
+ {
+ public const string Default = "[DISPID=0]";
+ }
+}
\ No newline at end of file
diff --git a/src/MsieJavaScriptEngine/HostItemBase.cs b/src/MsieJavaScriptEngine/HostItemBase.cs
new file mode 100644
index 0000000..ad1970f
--- /dev/null
+++ b/src/MsieJavaScriptEngine/HostItemBase.cs
@@ -0,0 +1,184 @@
+namespace MsieJavaScriptEngine
+{
+ using System;
+ using System.Globalization;
+ using System.Linq;
+ using System.Reflection;
+
+ ///
+ /// Base class of item, that implements interface
+ ///
+ internal abstract class HostItemBase : IReflect
+ {
+ ///
+ /// Target type
+ ///
+ protected readonly Type _type;
+
+ ///
+ /// Target object
+ ///
+ protected readonly object _target;
+
+ ///
+ /// JavaScript engine mode
+ ///
+ protected readonly JsEngineMode _engineMode;
+
+ ///
+ /// List of fields
+ ///
+ private readonly FieldInfo[] _fields;
+
+ ///
+ /// List of properties
+ ///
+ private readonly PropertyInfo[] _properties;
+
+ ///
+ /// List of methods
+ ///
+ private readonly MethodInfo[] _methods;
+
+ ///
+ /// Gets a target object
+ ///
+ public object Target
+ {
+ get { return _target; }
+ }
+
+
+ ///
+ /// Constructs an instance of the wrapper for item, that implements interface
+ ///
+ /// Target type
+ /// Target object
+ /// JavaScript engine mode
+ /// Flag for whether to allow access to members of the instance
+ protected HostItemBase(Type type, object target, JsEngineMode engineMode, bool instance)
+ {
+ _type = type;
+ _target = target;
+ _engineMode = engineMode;
+
+ BindingFlags bindingFlags = BindingFlags.Public;
+ if (instance)
+ {
+ bindingFlags |= BindingFlags.Instance;
+ }
+ else
+ {
+ bindingFlags |= BindingFlags.Static;
+ }
+
+ _fields = _type.GetFields(bindingFlags);
+ _properties = _type.GetProperties(bindingFlags);
+ _methods = _type.GetMethods(bindingFlags);
+ }
+
+
+ protected abstract object InnerInvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target,
+ object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters);
+
+ protected object InvokeStandardMember(string name, BindingFlags invokeAttr, Binder binder, object target,
+ object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
+ {
+ BindingFlags processedInvokeAttr = invokeAttr;
+ if ((processedInvokeAttr.HasFlag(BindingFlags.GetProperty)
+ || processedInvokeAttr.HasFlag(BindingFlags.PutDispProperty))
+ && !_properties.Any(p => p.Name == name)
+ && _fields.Any(p => p.Name == name))
+ {
+ if (processedInvokeAttr.HasFlag(BindingFlags.GetProperty))
+ {
+ processedInvokeAttr &= ~BindingFlags.GetProperty;
+ processedInvokeAttr |= BindingFlags.GetField;
+ }
+ else if (processedInvokeAttr.HasFlag(BindingFlags.PutDispProperty))
+ {
+ processedInvokeAttr &= ~BindingFlags.PutDispProperty;
+ processedInvokeAttr |= BindingFlags.SetField;
+ }
+ }
+
+ object result = _type.InvokeMember(name, processedInvokeAttr, binder, target,
+ args, modifiers, culture, namedParameters);
+
+ return result;
+ }
+
+ #region IReflect implementation
+
+ Type IReflect.UnderlyingSystemType
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+
+ FieldInfo IReflect.GetField(string name, BindingFlags bindingAttr)
+ {
+ FieldInfo field = _fields.SingleOrDefault(f => f.Name == name);
+
+ return field;
+ }
+
+ FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr)
+ {
+ return _fields;
+ }
+
+ MemberInfo[] IReflect.GetMember(string name, BindingFlags bindingAttr)
+ {
+ throw new NotImplementedException();
+ }
+
+ MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr)
+ {
+ throw new NotImplementedException();
+ }
+
+ MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr)
+ {
+ MethodInfo method = _methods.SingleOrDefault(m => m.Name == name);
+
+ return method;
+ }
+
+ MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)
+ {
+ throw new NotImplementedException();
+ }
+
+ MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr)
+ {
+ return _methods;
+ }
+
+ PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr)
+ {
+ return _properties;
+ }
+
+ PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr)
+ {
+ PropertyInfo property = _properties.SingleOrDefault(p => p.Name == name);
+
+ return property;
+ }
+
+ PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr, Binder binder,
+ Type returnType, Type[] types, ParameterModifier[] modifiers)
+ {
+ throw new NotImplementedException();
+ }
+
+ object IReflect.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target,
+ object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
+ {
+ return InnerInvokeMember(name, invokeAttr, binder,target, args, modifiers, culture, namedParameters);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/MsieJavaScriptEngine/HostObject.cs b/src/MsieJavaScriptEngine/HostObject.cs
index 44b8688..1c19248 100644
--- a/src/MsieJavaScriptEngine/HostObject.cs
+++ b/src/MsieJavaScriptEngine/HostObject.cs
@@ -5,172 +5,60 @@
using System.Linq;
using System.Reflection;
+ using Constants;
using Helpers;
///
/// Wrapper for object, that implements interface
///
- internal class HostObject : IReflect
+ internal sealed class HostObject : HostItemBase
{
- ///
- /// Target object
- ///
- private readonly object _target;
-
- ///
- /// Target type
- ///
- private readonly Type _type;
-
- ///
- /// JavaScript engine mode
- ///
- private readonly JsEngineMode _engineMode;
-
- ///
- /// List of fields
- ///
- private readonly FieldInfo[] _fields;
-
- ///
- /// List of properties
- ///
- private readonly PropertyInfo[] _properties;
-
- ///
- /// List of methods
- ///
- private readonly MethodInfo[] _methods;
-
- ///
- /// Gets a target object
- ///
- public object Target
- {
- get { return _target; }
- }
-
-
///
/// Constructs an instance of the wrapper for object, that implements interface
///
/// Target object
/// JavaScript engine mode
public HostObject(object target, JsEngineMode engineMode)
- {
- _target = target;
- _type = target.GetType();
- _engineMode = engineMode;
-
- var defaultBindingFlags = BindingFlags.Instance | BindingFlags.Public;
- _fields = _type.GetFields(defaultBindingFlags);
- _properties = _type.GetProperties(defaultBindingFlags);
- _methods = _type.GetMethods(defaultBindingFlags);
- }
-
- #region IReflect implementation
-
- Type IReflect.UnderlyingSystemType
- {
- get { throw new NotImplementedException(); }
- }
+ : base(target.GetType(), target, engineMode, true)
+ { }
- FieldInfo IReflect.GetField(string name, BindingFlags bindingAttr)
+ private object InvokeDelegate(Delegate del, object[] args)
{
- FieldInfo field = _fields.SingleOrDefault(f => f.Name == name);
-
- return field;
- }
-
- FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr)
- {
- return _fields;
- }
-
- MemberInfo[] IReflect.GetMember(string name, BindingFlags bindingAttr)
- {
- throw new NotImplementedException();
- }
-
- MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr)
- {
- throw new NotImplementedException();
- }
-
- MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr)
- {
- MethodInfo method = _methods.SingleOrDefault(m => m.Name == name);
-
- return method;
- }
-
- MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)
- {
- throw new NotImplementedException();
- }
+ if (del == null)
+ {
+ throw new ArgumentNullException("del");
+ }
- MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr)
- {
- return _methods;
- }
+ object[] processedArgs = args;
- PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr)
- {
- return _properties;
- }
+ if (_engineMode == JsEngineMode.Classic && processedArgs.Length > 0)
+ {
+ processedArgs = processedArgs.Skip(1).ToArray();
+ }
- PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr)
- {
- PropertyInfo property = _properties.SingleOrDefault(p => p.Name == name);
+ object result = del.DynamicInvoke(processedArgs);
- return property;
+ return result;
}
- PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr, Binder binder,
- Type returnType, Type[] types, ParameterModifier[] modifiers)
- {
- throw new NotImplementedException();
- }
+ #region HostItemBase implementation
- object IReflect.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target,
+ protected override object InnerInvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target,
object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
{
object result;
object processedTarget = TypeMappingHelpers.MapToHostType(target);
object[] processedArgs = TypeMappingHelpers.MapToHostType(args);
- var del = processedTarget as Delegate;
- if (del != null)
+ if (name == SpecialMemberName.Default && processedTarget is Delegate)
{
- if (_engineMode == JsEngineMode.Classic && processedArgs.Length > 0)
- {
- processedArgs = processedArgs.Skip(1).ToArray();
- }
-
- result = del.DynamicInvoke(processedArgs);
+ var del = (Delegate)processedTarget;
+ result = InvokeDelegate(del, processedArgs);
}
else
{
- BindingFlags processedInvokeAttr = invokeAttr;
- if ((processedInvokeAttr.HasFlag(BindingFlags.GetProperty)
- || processedInvokeAttr.HasFlag(BindingFlags.PutDispProperty))
- && !_properties.Any(p => p.Name == name)
- && _fields.Any(p => p.Name == name))
- {
- if (processedInvokeAttr.HasFlag(BindingFlags.GetProperty))
- {
- processedInvokeAttr &= ~BindingFlags.GetProperty;
- processedInvokeAttr |= BindingFlags.GetField;
- }
- else if (processedInvokeAttr.HasFlag(BindingFlags.PutDispProperty))
- {
- processedInvokeAttr &= ~BindingFlags.PutDispProperty;
- processedInvokeAttr |= BindingFlags.SetField;
- }
- }
-
- result = _type.InvokeMember(name, processedInvokeAttr, binder, processedTarget,
+ result = InvokeStandardMember(name, invokeAttr, binder, processedTarget,
processedArgs, modifiers, culture, namedParameters);
}
diff --git a/src/MsieJavaScriptEngine/HostType.cs b/src/MsieJavaScriptEngine/HostType.cs
new file mode 100644
index 0000000..4cdbf31
--- /dev/null
+++ b/src/MsieJavaScriptEngine/HostType.cs
@@ -0,0 +1,54 @@
+namespace MsieJavaScriptEngine
+{
+ using System;
+ using System.Globalization;
+ using System.Linq;
+ using System.Reflection;
+
+ using Constants;
+ using Helpers;
+
+ ///
+ /// Wrapper for type, that implements interface
+ ///
+ internal sealed class HostType : HostItemBase
+ {
+ ///
+ /// Constructs an instance of the wrapper for type, that implements interface
+ ///
+ /// Target type
+ /// JavaScript engine mode
+ public HostType(Type type, JsEngineMode engineMode)
+ : base(type, null, engineMode, false)
+ { }
+
+
+ #region HostItemBase implementation
+
+ protected override object InnerInvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target,
+ object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
+ {
+ object[] processedArgs = TypeMappingHelpers.MapToHostType(args);
+ object result;
+
+ if (name == SpecialMemberName.Default && invokeAttr.HasFlag(BindingFlags.CreateInstance))
+ {
+ if (_engineMode != JsEngineMode.Classic && processedArgs.Length > 0)
+ {
+ processedArgs = processedArgs.Skip(1).ToArray();
+ }
+
+ result = Activator.CreateInstance(_type, processedArgs);
+ }
+ else
+ {
+ result = InvokeStandardMember(name, invokeAttr, binder, target,
+ processedArgs, modifiers, culture, namedParameters);
+ }
+
+ return TypeMappingHelpers.MapToScriptType(result, _engineMode);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/MsieJavaScriptEngine/IInnerJsEngine.cs b/src/MsieJavaScriptEngine/IInnerJsEngine.cs
index 205494d..a733dc1 100644
--- a/src/MsieJavaScriptEngine/IInnerJsEngine.cs
+++ b/src/MsieJavaScriptEngine/IInnerJsEngine.cs
@@ -68,5 +68,16 @@ internal interface IInnerJsEngine : IDisposable
/// The object to expose
/// Allows to embed instances of simple classes (or structures) and delegates.
void EmbedHostObject(string itemName, object value);
+
+ ///
+ /// Embeds a host type to script code
+ ///
+ /// The name for the new global variable that will represent the type
+ /// The type to expose
+ ///
+ /// Host types are exposed to script code in the form of objects whose properties and
+ /// methods are bound to the type's static members.
+ ///
+ void EmbedHostType(string itemName, Type type);
}
}
\ No newline at end of file
diff --git a/src/MsieJavaScriptEngine/JsRt/ChakraJsRtJsEngineBase.cs b/src/MsieJavaScriptEngine/JsRt/ChakraJsRtJsEngineBase.cs
index f0c17d9..ca92f4e 100644
--- a/src/MsieJavaScriptEngine/JsRt/ChakraJsRtJsEngineBase.cs
+++ b/src/MsieJavaScriptEngine/JsRt/ChakraJsRtJsEngineBase.cs
@@ -1,5 +1,7 @@
namespace MsieJavaScriptEngine.JsRt
{
+ using System;
+
using Helpers;
///
@@ -76,6 +78,8 @@ protected void StartDebugging()
public abstract void EmbedHostObject(string itemName, object value);
+ public abstract void EmbedHostType(string itemName, Type type);
+
#endregion
#region IDisposable implementation
diff --git a/src/MsieJavaScriptEngine/JsRt/Edge/ChakraEdgeJsRtJsEngine.cs b/src/MsieJavaScriptEngine/JsRt/Edge/ChakraEdgeJsRtJsEngine.cs
index f014f31..a63bbb2 100644
--- a/src/MsieJavaScriptEngine/JsRt/Edge/ChakraEdgeJsRtJsEngine.cs
+++ b/src/MsieJavaScriptEngine/JsRt/Edge/ChakraEdgeJsRtJsEngine.cs
@@ -507,6 +507,17 @@ public override void EmbedHostObject(string itemName, object value)
});
}
+ public override void EmbedHostType(string itemName, Type type)
+ {
+ InvokeScript(() =>
+ {
+ EdgeJsValue typeValue = EdgeJsValue.FromObject(new HostType(type, _engineMode));
+ EdgeJsPropertyId itemId = EdgeJsPropertyId.FromString(itemName);
+
+ EdgeJsValue.GlobalObject.SetProperty(itemId, typeValue, true);
+ });
+ }
+
#endregion
#region IDisposable implementation
diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/ChakraIeJsRtJsEngine.cs b/src/MsieJavaScriptEngine/JsRt/Ie/ChakraIeJsRtJsEngine.cs
index f65ebae..1696b76 100644
--- a/src/MsieJavaScriptEngine/JsRt/Ie/ChakraIeJsRtJsEngine.cs
+++ b/src/MsieJavaScriptEngine/JsRt/Ie/ChakraIeJsRtJsEngine.cs
@@ -543,6 +543,17 @@ public override void EmbedHostObject(string itemName, object value)
});
}
+ public override void EmbedHostType(string itemName, Type type)
+ {
+ InvokeScript(() =>
+ {
+ IeJsValue typeValue = IeJsValue.FromObject(new HostType(type, _engineMode));
+ IeJsPropertyId itemId = IeJsPropertyId.FromString(itemName);
+
+ IeJsValue.GlobalObject.SetProperty(itemId, typeValue, true);
+ });
+ }
+
#endregion
#region IDisposable implementation
diff --git a/src/MsieJavaScriptEngine/MsieJavaScriptEngine.csproj b/src/MsieJavaScriptEngine/MsieJavaScriptEngine.csproj
index 514cbf7..ad6c1b0 100644
--- a/src/MsieJavaScriptEngine/MsieJavaScriptEngine.csproj
+++ b/src/MsieJavaScriptEngine/MsieJavaScriptEngine.csproj
@@ -44,7 +44,6 @@
-
@@ -67,12 +66,15 @@
+
+
+
diff --git a/src/MsieJavaScriptEngine/MsieJsEngine.cs b/src/MsieJavaScriptEngine/MsieJsEngine.cs
index 7102273..db1ce4e 100644
--- a/src/MsieJavaScriptEngine/MsieJsEngine.cs
+++ b/src/MsieJavaScriptEngine/MsieJsEngine.cs
@@ -658,7 +658,7 @@ public void EmbedHostObject(string itemName, object value)
|| itemType == typeof (Undefined))
{
throw new NotSupportedTypeException(
- string.Format(Strings.Runtime_EmbeddedHostObjectTypeNotSupported, itemType.FullName));
+ string.Format(Strings.Runtime_EmbeddedHostObjectTypeNotSupported, itemName, itemType.FullName));
}
}
else
@@ -669,6 +669,48 @@ public void EmbedHostObject(string itemName, object value)
_jsEngine.EmbedHostObject(itemName, value);
}
+ ///
+ /// Embeds a host type to script code
+ ///
+ /// The name for the new global variable that will represent the type
+ /// The type to expose
+ ///
+ /// Host types are exposed to script code in the form of objects whose properties and
+ /// methods are bound to the type's static members.
+ ///
+ public void EmbedHostType(string itemName, Type type)
+ {
+ VerifyNotDisposed();
+
+ if (string.IsNullOrWhiteSpace(itemName))
+ {
+ throw new ArgumentException(
+ string.Format(Strings.Common_ArgumentIsEmpty, "itemName"), "itemName");
+ }
+
+ if (!ValidationHelpers.CheckNameFormat(itemName))
+ {
+ throw new FormatException(
+ string.Format(Strings.Runtime_InvalidScriptItemNameFormat, itemName));
+ }
+
+ if (type != null)
+ {
+ if (ValidationHelpers.IsPrimitiveType(type)
+ || type == typeof(Undefined))
+ {
+ throw new NotSupportedTypeException(
+ string.Format(Strings.Runtime_EmbeddedHostTypeNotSupported, type.FullName));
+ }
+ }
+ else
+ {
+ throw new ArgumentNullException("type", string.Format(Strings.Common_ArgumentIsNull, "type"));
+ }
+
+ _jsEngine.EmbedHostType(itemName, type);
+ }
+
#region IDisposable implementation
///
diff --git a/src/MsieJavaScriptEngine/Resources/Strings.Designer.cs b/src/MsieJavaScriptEngine/Resources/Strings.Designer.cs
index d0b7699..e8a951e 100644
--- a/src/MsieJavaScriptEngine/Resources/Strings.Designer.cs
+++ b/src/MsieJavaScriptEngine/Resources/Strings.Designer.cs
@@ -234,6 +234,15 @@ internal static string Runtime_EmbeddedHostObjectTypeNotSupported {
}
}
+ ///
+ /// Looks up a localized string similar to The embedded host type `{0}` is not supported..
+ ///
+ internal static string Runtime_EmbeddedHostTypeNotSupported {
+ get {
+ return ResourceManager.GetString("Runtime_EmbeddedHostTypeNotSupported", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to The function name '{0}' is forbidden, as is contained in the list of reserved words of JavaScript language..
///
diff --git a/src/MsieJavaScriptEngine/Resources/Strings.resx b/src/MsieJavaScriptEngine/Resources/Strings.resx
index a41d230..a9b9aca 100644
--- a/src/MsieJavaScriptEngine/Resources/Strings.resx
+++ b/src/MsieJavaScriptEngine/Resources/Strings.resx
@@ -177,6 +177,9 @@ See more details:
The embedded host object '{0}' has a type `{1}`, which is not supported.
+
+ The embedded host type `{0}` is not supported.
+
The function name '{0}' is forbidden, as is contained in the list of reserved words of JavaScript language.
diff --git a/src/MsieJavaScriptEngine/Resources/Strings.ru-ru.resx b/src/MsieJavaScriptEngine/Resources/Strings.ru-ru.resx
index bde2624..cc53d1f 100644
--- a/src/MsieJavaScriptEngine/Resources/Strings.ru-ru.resx
+++ b/src/MsieJavaScriptEngine/Resources/Strings.ru-ru.resx
@@ -177,6 +177,9 @@
Встраиваемый объекта хоста "{0}" имеет тип `{1}`, который не поддерживается!
+
+ Встраиваемый тип хоста `{0}` не поддерживается!
+
Имя функции "{0}" запрещено, т.к. входит в список зарезервированных слов JavaScript!
diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/Base64Encoder.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Base64Encoder.cs
new file mode 100644
index 0000000..bbc5e6f
--- /dev/null
+++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Base64Encoder.cs
@@ -0,0 +1,16 @@
+namespace MsieJavaScriptEngine.Test.Common.Interop
+{
+ using System;
+ using System.Text;
+
+ public static class Base64Encoder
+ {
+ public const int DATA_URI_MAX = 32768;
+
+
+ public static string Encode(string value)
+ {
+ return Convert.ToBase64String(Encoding.Default.GetBytes(value));
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/BundleTable.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/BundleTable.cs
new file mode 100644
index 0000000..2b7d887
--- /dev/null
+++ b/test/MsieJavaScriptEngine.Test.Common/Interop/BundleTable.cs
@@ -0,0 +1,19 @@
+namespace MsieJavaScriptEngine.Test.Common.Interop
+{
+ public static class BundleTable
+ {
+ private static bool _enableOptimizations = true;
+
+ public static bool EnableOptimizations
+ {
+ get
+ {
+ return _enableOptimizations;
+ }
+ set
+ {
+ _enableOptimizations = value;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/Date.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Date.cs
index 73da9de..c6c4be4 100644
--- a/test/MsieJavaScriptEngine.Test.Common/Interop/Date.cs
+++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Date.cs
@@ -1,5 +1,7 @@
namespace MsieJavaScriptEngine.Test.Common.Interop
{
+ using System;
+
public struct Date
{
private static readonly int[] _cumulativeDays = { 0, 31, 59, 90, 120, 151, 181,
@@ -9,6 +11,17 @@ public struct Date
public int Month;
public int Day;
+ public static Date Today
+ {
+ get
+ {
+ DateTime currentDateTime = DateTime.Today;
+ Date currentDate = new Date(currentDateTime.Year, currentDateTime.Month, currentDateTime.Day);
+
+ return currentDate;
+ }
+ }
+
public Date(int year, int month, int day)
{
diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/Person.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Person.cs
index 294a17c..c5d3862 100644
--- a/test/MsieJavaScriptEngine.Test.Common/Interop/Person.cs
+++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Person.cs
@@ -24,5 +24,11 @@ public Person(string firstName, string lastName)
FirstName = firstName;
LastName = lastName;
}
+
+
+ public override string ToString()
+ {
+ return string.Format("{{FirstName={0},LastName={1}}}", FirstName, LastName);
+ }
}
}
\ No newline at end of file
diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/Point3D.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Point3D.cs
new file mode 100644
index 0000000..ee76d89
--- /dev/null
+++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Point3D.cs
@@ -0,0 +1,25 @@
+namespace MsieJavaScriptEngine.Test.Common.Interop
+{
+ public struct Point3D
+ {
+ public int X;
+ public int Y;
+ public int Z;
+
+ public static readonly Point3D Empty = new Point3D();
+
+
+ public Point3D(int x, int y, int z)
+ {
+ X = x;
+ Y = y;
+ Z = z;
+ }
+
+
+ public override string ToString()
+ {
+ return string.Format("{{X={0},Y={1},Z={2}}}", X, Y, Z);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/PredefinedStrings.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/PredefinedStrings.cs
new file mode 100644
index 0000000..efca32e
--- /dev/null
+++ b/test/MsieJavaScriptEngine.Test.Common/Interop/PredefinedStrings.cs
@@ -0,0 +1,9 @@
+namespace MsieJavaScriptEngine.Test.Common.Interop
+{
+ public struct PredefinedStrings
+ {
+ public const string VeryLongName = "Very Long Name";
+ public const string AnotherVeryLongName = "Another Very Long Name";
+ public const string TheLastVeryLongName = "The Last Very Long Name";
+ }
+}
\ No newline at end of file
diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/SimpleSingleton.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/SimpleSingleton.cs
new file mode 100644
index 0000000..14d1ed4
--- /dev/null
+++ b/test/MsieJavaScriptEngine.Test.Common/Interop/SimpleSingleton.cs
@@ -0,0 +1,17 @@
+namespace MsieJavaScriptEngine.Test.Common.Interop
+{
+ public class SimpleSingleton
+ {
+ public static readonly SimpleSingleton Instance = new SimpleSingleton();
+
+
+ private SimpleSingleton()
+ { }
+
+
+ public override string ToString()
+ {
+ return "[simple singleton]";
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/MsieJavaScriptEngine.Test.Common/InteropTestsBase.cs b/test/MsieJavaScriptEngine.Test.Common/InteropTestsBase.cs
index cdbae5c..8f57899 100644
--- a/test/MsieJavaScriptEngine.Test.Common/InteropTestsBase.cs
+++ b/test/MsieJavaScriptEngine.Test.Common/InteropTestsBase.cs
@@ -486,5 +486,486 @@ public virtual void InteractionOfEmbeddedCustomValueTypeAndDelegateInstancesIsCo
#endregion
#endregion
+
+
+ #region Embedding of types
+
+ #region Creating of instances
+
+ [Test]
+ public virtual void CreatingAnInstanceOfEmbeddedBuiltinValueTypeIsCorrect()
+ {
+ // Arrange
+ Type pointType = typeof(Point);
+
+ const string input = "(new Point()).ToString()";
+ const string targetOutput = "{X=0,Y=0}";
+
+ // Act
+ string output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("Point", pointType);
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ [Test]
+ public virtual void CreatingAnInstanceOfEmbeddedBuiltinReferenceTypeIsCorrect()
+ {
+ // Arrange
+ Type uriType = typeof(Uri);
+
+ const string input = @"var baseUri = new Uri('https://github.com'),
+ relativeUri = 'Taritsyn/MsieJavaScriptEngine'
+ ;
+
+(new Uri(baseUri, relativeUri)).ToString()";
+ const string targetOutput = "https://github.com/Taritsyn/MsieJavaScriptEngine";
+
+ // Act
+ string output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("Uri", uriType);
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ [Test]
+ public virtual void CreatingAnInstanceOfEmbeddedCustomValueTypeIsCorrect()
+ {
+ // Arrange
+ Type point3DType = typeof(Point3D);
+
+ const string input = "(new Point3D(2, 5, 14)).ToString()";
+ const string targetOutput = "{X=2,Y=5,Z=14}";
+
+ // Act
+ string output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("Point3D", point3DType);
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ [Test]
+ public virtual void CreatingAnInstanceOfEmbeddedCustomReferenceTypeIsCorrect()
+ {
+ // Arrange
+ Type personType = typeof(Person);
+
+ const string input = "(new Person('Vanya', 'Tutkin')).ToString()";
+ const string targetOutput = "{FirstName=Vanya,LastName=Tutkin}";
+
+ // Act
+ string output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("Person", personType);
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ #endregion
+
+ #region Types with constants
+
+ [Test]
+ public virtual void EmbeddingOfBuiltinReferenceTypeWithConstantsIsCorrect()
+ {
+ // Arrange
+ Type mathType = typeof(Math);
+
+ const string input1 = "Math2.PI";
+ const double targetOutput1 = 3.1415926535897931d;
+
+ const string input2 = "Math2.E";
+ const double targetOutput2 = 2.7182818284590451d;
+
+ // Act
+ double output1;
+ double output2;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("Math2", mathType);
+
+ output1 = jsEngine.Evaluate(input1);
+ output2 = jsEngine.Evaluate(input2);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput1, output1);
+ Assert.AreEqual(targetOutput2, output2);
+ }
+
+ [Test]
+ public virtual void EmbeddingOfCustomValueTypeWithConstantsIsCorrect()
+ {
+ // Arrange
+ Type predefinedStringsType = typeof(PredefinedStrings);
+
+ const string input1 = "PredefinedStrings.VeryLongName";
+ const string targetOutput1 = "Very Long Name";
+
+ const string input2 = "PredefinedStrings.AnotherVeryLongName";
+ const string targetOutput2 = "Another Very Long Name";
+
+ const string input3 = "PredefinedStrings.TheLastVeryLongName";
+ const string targetOutput3 = "The Last Very Long Name";
+
+ // Act
+ string output1;
+ string output2;
+ string output3;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("PredefinedStrings", predefinedStringsType);
+
+ output1 = jsEngine.Evaluate(input1);
+ output2 = jsEngine.Evaluate(input2);
+ output3 = jsEngine.Evaluate(input3);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput1, output1);
+ Assert.AreEqual(targetOutput2, output2);
+ Assert.AreEqual(targetOutput3, output3);
+ }
+
+ [Test]
+ public virtual void EmbeddingOfCustomReferenceTypeWithConstantIsCorrect()
+ {
+ // Arrange
+ Type base64EncoderType = typeof(Base64Encoder);
+
+ const string input = "Base64Encoder.DATA_URI_MAX";
+ const int targetOutput = 32768;
+
+ // Act
+ int output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("Base64Encoder", base64EncoderType);
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ #endregion
+
+ #region Types with fields
+
+ [Test]
+ public virtual void EmbeddingOfBuiltinValueTypeWithFieldIsCorrect()
+ {
+ // Arrange
+ Type guidType = typeof(Guid);
+
+ const string input = "Guid.Empty.ToString()";
+ const string targetOutput = "00000000-0000-0000-0000-000000000000";
+
+ // Act
+ string output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("Guid", guidType);
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ [Test]
+ public virtual void EmbeddingOfBuiltinReferenceTypeWithFieldIsCorrect()
+ {
+ // Arrange
+ Type bitConverterType = typeof(BitConverter);
+
+ const string input = "BitConverter.IsLittleEndian";
+ const bool targetOutput = true;
+
+ // Act
+ bool output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("BitConverter", bitConverterType);
+ output = (bool)jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ [Test]
+ public virtual void EmbeddingOfCustomValueTypeWithFieldIsCorrect()
+ {
+ // Arrange
+ Type point3DType = typeof(Point3D);
+
+ const string input = "Point3D.Empty.ToString()";
+ const string targetOutput = "{X=0,Y=0,Z=0}";
+
+ // Act
+ string output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("Point3D", point3DType);
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ [Test]
+ public virtual void EmbeddingOfCustomReferenceTypeWithFieldIsCorrect()
+ {
+ // Arrange
+ Type simpleSingletonType = typeof(SimpleSingleton);
+
+ const string input = "SimpleSingleton.Instance.ToString()";
+ const string targetOutput = "[simple singleton]";
+
+ // Act
+ string output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("SimpleSingleton", simpleSingletonType);
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ #endregion
+
+ #region Types with properties
+
+ [Test]
+ public virtual void EmbeddingOfBuiltinValueTypeWithPropertyIsCorrect()
+ {
+ // Arrange
+ Type colorType = typeof(Color);
+
+ const string input = "Color.OrangeRed.ToString()";
+ const string targetOutput = "Color [OrangeRed]";
+
+ // Act
+ string output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("Color", colorType);
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ [Test]
+ public virtual void EmbeddingOfBuiltinReferenceTypeWithPropertyIsCorrect()
+ {
+ // Arrange
+ Type environmentType = typeof(Environment);
+
+ const string input = "Environment.NewLine";
+ string[] targetOutput = { "\r", "\r\n", "\n", "\n\r" };
+
+ // Act
+ string output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("Environment", environmentType);
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.IsTrue(targetOutput.Contains(output));
+ }
+
+ [Test]
+ public virtual void EmbeddingOfCustomValueTypeWithPropertyIsCorrect()
+ {
+ // Arrange
+ Type dateType = typeof(Date);
+
+ const string initCode = "var currentDate = Date2.Today;";
+
+ const string inputYear = "currentDate.Year";
+ const string inputMonth = "currentDate.Month";
+ const string inputDay = "currentDate.Day";
+
+ DateTime targetOutput = DateTime.Today;
+
+ // Act
+ DateTime output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("Date2", dateType);
+ jsEngine.Execute(initCode);
+
+ var outputYear = jsEngine.Evaluate(inputYear);
+ var outputMonth = jsEngine.Evaluate(inputMonth);
+ var outputDay = jsEngine.Evaluate(inputDay);
+
+ output = new DateTime(outputYear, outputMonth, outputDay);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ [Test]
+ public virtual void EmbeddingOfCustomReferenceTypeWithPropertyIsCorrect()
+ {
+ // Arrange
+ Type bundleTableType = typeof(BundleTable);
+ const string updateCode = "BundleTable.EnableOptimizations = false;";
+
+ const string input = "BundleTable.EnableOptimizations";
+ const bool targetOutput = false;
+
+ // Act
+ bool output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("BundleTable", bundleTableType);
+ jsEngine.Execute(updateCode);
+
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ #endregion
+
+ #region Types with methods
+
+ [Test]
+ public virtual void EmbeddingOfBuiltinValueTypeWithMethodIsCorrect()
+ {
+ // Arrange
+ Type dateTimeType = typeof(DateTime);
+
+ const string input = "DateTime.DaysInMonth(2016, 2)";
+ const int targetOutput = 29;
+
+ // Act
+ int output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("DateTime", dateTimeType);
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ [Test]
+ public virtual void EmbeddingOfBuiltinReferenceTypeWithMethodIsCorrect()
+ {
+ // Arrange
+ Type mathType = typeof(Math);
+
+ const string input = "Math2.Max(5.37, 5.56)";
+ const double targetOutput = 5.56;
+
+ // Act
+ double output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("Math2", mathType);
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ [Test]
+ public virtual void EmbeddingOfCustomValueTypeWithMethodIsCorrect()
+ {
+ // Arrange
+ var dateType = typeof(Date);
+
+ const string input = "Date2.IsLeapYear(2016)";
+ const bool targetOutput = true;
+
+ // Act
+ bool output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("Date2", dateType);
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ [Test]
+ public virtual void EmbeddingOfCustomReferenceTypeWithMethodIsCorrect()
+ {
+ // Arrange
+ Type base64EncoderType = typeof(Base64Encoder);
+
+ const string input = "Base64Encoder.Encode('https://github.com/Taritsyn/MsieJavaScriptEngine')";
+ const string targetOutput = "aHR0cHM6Ly9naXRodWIuY29tL1Rhcml0c3luL01zaWVKYXZhU2NyaXB0RW5naW5l";
+
+ // Act
+ string output;
+
+ using (var jsEngine = CreateJsEngine())
+ {
+ jsEngine.EmbedHostType("Base64Encoder", base64EncoderType);
+ output = jsEngine.Evaluate(input);
+ }
+
+ // Assert
+ Assert.AreEqual(targetOutput, output);
+ }
+
+ #endregion
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/test/MsieJavaScriptEngine.Test.Common/MsieJavaScriptEngine.Test.Common.csproj b/test/MsieJavaScriptEngine.Test.Common/MsieJavaScriptEngine.Test.Common.csproj
index b506e69..c724905 100644
--- a/test/MsieJavaScriptEngine.Test.Common/MsieJavaScriptEngine.Test.Common.csproj
+++ b/test/MsieJavaScriptEngine.Test.Common/MsieJavaScriptEngine.Test.Common.csproj
@@ -44,11 +44,16 @@
+
+
+
+
+