Skip to content

Commit f96174f

Browse files
Version 5.3.8: Added dynamic host object projection (Issue ClearFoundry#3), added support for overloaded indexers and indexed properties, interface targets now expose System.Object members, expanded caching for improved performance and memory usage, added many new tests, migrated to new V8 Persistent API. Tested with V8 3.22.11.
1 parent 6d9fc28 commit f96174f

33 files changed

+1977
-255
lines changed

ClearScript/Exports/VersionSymbols.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,5 @@
6363

6464
#pragma once
6565

66-
#define CLEARSCRIPT_VERSION_STRING "5.3.7.0"
67-
#define CLEARSCRIPT_VERSION_COMMA_SEPARATED 5,3,7,0
66+
#define CLEARSCRIPT_VERSION_STRING "5.3.8.0"
67+
#define CLEARSCRIPT_VERSION_COMMA_SEPARATED 5,3,8,0

ClearScript/HostFunctions.cs

Lines changed: 234 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,10 @@
6161

6262
using System;
6363
using System.Diagnostics.CodeAnalysis;
64+
using System.Dynamic;
6465
using System.Globalization;
6566
using System.Linq;
67+
using System.Linq.Expressions;
6668
using Microsoft.ClearScript.Util;
6769

6870
namespace Microsoft.ClearScript
@@ -100,9 +102,9 @@ public HostFunctions()
100102
/// </summary>
101103
/// <returns>A new empty host object.</returns>
102104
/// <remarks>
103-
/// This function is provided for script languages that support "expando" functionality.
104-
/// It creates an object that supports dynamic property addition and removal. The host
105-
/// can manipulate it via the <see cref="IPropertyBag"/> interface.
105+
/// This function is provided for script languages that do not support external
106+
/// instantiation. It creates an object that supports dynamic property addition and
107+
/// removal. The host can manipulate it via the <see cref="IPropertyBag"/> interface.
106108
/// </remarks>
107109
/// <example>
108110
/// The following code creates an empty host object and adds several properties to it.
@@ -127,8 +129,9 @@ public PropertyBag newObj()
127129
/// <param name="args">Optional constructor arguments.</param>
128130
/// <returns>A new host object of the specified type.</returns>
129131
/// <remarks>
130-
/// For information about the mapping between host members and script-callable properties
131-
/// and methods, see
132+
/// This function is provided for script languages that do not support external
133+
/// instantiation. For information about the mapping between host members and script-
134+
/// callable properties and methods, see
132135
/// <see cref="ScriptEngine.AddHostObject(string, HostItemFlags, object)">AddHostObject</see>.
133136
/// </remarks>
134137
/// <example>
@@ -151,6 +154,29 @@ public T newObj<T>(params object[] args)
151154
return (T)typeof(T).CreateInstance(args);
152155
}
153156

157+
/// <summary>
158+
/// Performs dynamic instantiation.
159+
/// </summary>
160+
/// <param name="target">The dynamic host object that provides the instantiation operation to perform.</param>
161+
/// <param name="args">Optional instantiation arguments.</param>
162+
/// <returns>The result of the operation, which is usually a new dynamic host object.</returns>
163+
/// <remarks>
164+
/// This function is provided for script languages that do not support external
165+
/// instantiation.
166+
/// </remarks>
167+
public object newObj(IDynamicMetaObjectProvider target, params object[] args)
168+
{
169+
MiscHelpers.VerifyNonNullArgument(target, "target");
170+
171+
object result;
172+
if (target.GetMetaObject(Expression.Constant(target)).TryCreateInstance(args, out result))
173+
{
174+
return result;
175+
}
176+
177+
throw new InvalidOperationException("Invalid dynamic instantiation");
178+
}
179+
154180
/// <summary>
155181
/// Creates a host array.
156182
/// </summary>
@@ -1000,6 +1026,209 @@ public object toDecimal(IConvertible value)
10001026
return HostObject.Wrap(Convert.ToDecimal(value));
10011027
}
10021028

1029+
/// <summary>
1030+
/// Gets the value of a property in a dynamic host object that implements <see cref="IPropertyBag"/>.
1031+
/// </summary>
1032+
/// <param name="target">The dynamic host object that contains the property to get.</param>
1033+
/// <param name="name">The name of the property to get.</param>
1034+
/// <returns>The value of the specified property.</returns>
1035+
/// <remarks>
1036+
/// This function is provided for script languages that do not support dynamic properties.
1037+
/// </remarks>
1038+
public object getProperty(IPropertyBag target, string name)
1039+
{
1040+
MiscHelpers.VerifyNonNullArgument(target, "target");
1041+
1042+
object result;
1043+
if (target.TryGetValue(name, out result))
1044+
{
1045+
return result;
1046+
}
1047+
1048+
return Nonexistent.Value;
1049+
}
1050+
1051+
/// <summary>
1052+
/// Sets a property value in a dynamic host object that implements <see cref="IPropertyBag"/>.
1053+
/// </summary>
1054+
/// <param name="target">The dynamic host object that contains the property to set.</param>
1055+
/// <param name="name">The name of the property to set.</param>
1056+
/// <param name="value">The new value of the specified property.</param>
1057+
/// <returns>The result of the operation, which is usually the value assigned to the specified property.</returns>
1058+
/// <remarks>
1059+
/// This function is provided for script languages that do not support dynamic properties.
1060+
/// </remarks>
1061+
public object setProperty(IPropertyBag target, string name, object value)
1062+
{
1063+
MiscHelpers.VerifyNonNullArgument(target, "target");
1064+
return target[name] = value;
1065+
}
1066+
1067+
/// <summary>
1068+
/// Removes a property from a dynamic host object that implements <see cref="IPropertyBag"/>.
1069+
/// </summary>
1070+
/// <param name="target">The dynamic host object that contains the property to remove.</param>
1071+
/// <param name="name">The name of the property to remove.</param>
1072+
/// <returns><c>True</c> if the property was found and removed, <c>false</c> otherwise.</returns>
1073+
/// <remarks>
1074+
/// This function is provided for script languages that do not support dynamic properties.
1075+
/// </remarks>
1076+
public bool removeProperty(IPropertyBag target, string name)
1077+
{
1078+
MiscHelpers.VerifyNonNullArgument(target, "target");
1079+
return target.Remove(name);
1080+
}
1081+
1082+
/// <summary>
1083+
/// Gets the value of a property in a dynamic host object that implements <see cref="IDynamicMetaObjectProvider"/>.
1084+
/// </summary>
1085+
/// <param name="target">The dynamic host object that contains the property to get.</param>
1086+
/// <param name="name">The name of the property to get.</param>
1087+
/// <returns>The value of the specified property.</returns>
1088+
/// <remarks>
1089+
/// This function is provided for script languages that do not support dynamic properties.
1090+
/// </remarks>
1091+
public object getProperty(IDynamicMetaObjectProvider target, string name)
1092+
{
1093+
MiscHelpers.VerifyNonNullArgument(target, "target");
1094+
1095+
object result;
1096+
if (target.GetMetaObject(Expression.Constant(target)).TryGetMember(name, out result))
1097+
{
1098+
return result;
1099+
}
1100+
1101+
return Nonexistent.Value;
1102+
}
1103+
1104+
/// <summary>
1105+
/// Sets a property value in a dynamic host object that implements <see cref="IDynamicMetaObjectProvider"/>.
1106+
/// </summary>
1107+
/// <param name="target">The dynamic host object that contains the property to set.</param>
1108+
/// <param name="name">The name of the property to set.</param>
1109+
/// <param name="value">The new value of the specified property.</param>
1110+
/// <returns>The result of the operation, which is usually the value assigned to the specified property.</returns>
1111+
/// <remarks>
1112+
/// This function is provided for script languages that do not support dynamic properties.
1113+
/// </remarks>
1114+
public object setProperty(IDynamicMetaObjectProvider target, string name, object value)
1115+
{
1116+
MiscHelpers.VerifyNonNullArgument(target, "target");
1117+
1118+
object result;
1119+
if (target.GetMetaObject(Expression.Constant(target)).TrySetMember(name, value, out result))
1120+
{
1121+
return result;
1122+
}
1123+
1124+
throw new InvalidOperationException("Invalid dynamic property assignment");
1125+
}
1126+
1127+
/// <summary>
1128+
/// Removes a property from a dynamic host object that implements <see cref="IDynamicMetaObjectProvider"/>.
1129+
/// </summary>
1130+
/// <param name="target">The dynamic host object that contains the property to remove.</param>
1131+
/// <param name="name">The name of the property to remove.</param>
1132+
/// <returns><c>True</c> if the property was found and removed, <c>false</c> otherwise.</returns>
1133+
/// <remarks>
1134+
/// This function is provided for script languages that do not support dynamic properties.
1135+
/// </remarks>
1136+
public bool removeProperty(IDynamicMetaObjectProvider target, string name)
1137+
{
1138+
MiscHelpers.VerifyNonNullArgument(target, "target");
1139+
1140+
bool result;
1141+
if (target.GetMetaObject(Expression.Constant(target)).TryDeleteMember(name, out result))
1142+
{
1143+
return result;
1144+
}
1145+
1146+
throw new InvalidOperationException("Invalid dynamic property deletion");
1147+
}
1148+
1149+
/// <summary>
1150+
/// Gets the value of an element in a dynamic host object that implements <see cref="IDynamicMetaObjectProvider"/>.
1151+
/// </summary>
1152+
/// <param name="target">The dynamic host object that contains the element to get.</param>
1153+
/// <param name="indices">One or more indices that identify the element to get.</param>
1154+
/// <returns>The value of the specified element.</returns>
1155+
/// <remarks>
1156+
/// This function is provided for script languages that do not support general indexing.
1157+
/// </remarks>
1158+
public object getElement(IDynamicMetaObjectProvider target, params object[] indices)
1159+
{
1160+
MiscHelpers.VerifyNonNullArgument(target, "target");
1161+
1162+
object result;
1163+
if (target.GetMetaObject(Expression.Constant(target)).TryGetIndex(indices, out result))
1164+
{
1165+
return result;
1166+
}
1167+
1168+
return Nonexistent.Value;
1169+
1170+
}
1171+
1172+
/// <summary>
1173+
/// Sets an element value in a dynamic host object that implements <see cref="IDynamicMetaObjectProvider"/>.
1174+
/// </summary>
1175+
/// <param name="target">The dynamic host object that contains the element to set.</param>
1176+
/// <param name="value">The new value of the element.</param>
1177+
/// <param name="indices">One or more indices that identify the element to set.</param>
1178+
/// <returns>The result of the operation, which is usually the value assigned to the specified element.</returns>
1179+
/// <remarks>
1180+
/// This function is provided for script languages that do not support general indexing.
1181+
/// </remarks>
1182+
public object setElement(IDynamicMetaObjectProvider target, object value, params object[] indices)
1183+
{
1184+
MiscHelpers.VerifyNonNullArgument(target, "target");
1185+
1186+
object result;
1187+
if (target.GetMetaObject(Expression.Constant(target)).TrySetIndex(indices, value, out result))
1188+
{
1189+
return result;
1190+
}
1191+
1192+
throw new InvalidOperationException("Invalid dynamic element assignment");
1193+
}
1194+
1195+
/// <summary>
1196+
/// Removes an element from a dynamic host object that implements <see cref="IDynamicMetaObjectProvider"/>.
1197+
/// </summary>
1198+
/// <param name="target">The dynamic host object that contains the element to remove.</param>
1199+
/// <param name="indices">One or more indices that identify the element to remove.</param>
1200+
/// <returns><c>True</c> if the element was found and removed, <c>false</c> otherwise.</returns>
1201+
/// <remarks>
1202+
/// This function is provided for script languages that do not support general indexing.
1203+
/// </remarks>
1204+
public bool removeElement(IDynamicMetaObjectProvider target, params object[] indices)
1205+
{
1206+
MiscHelpers.VerifyNonNullArgument(target, "target");
1207+
1208+
bool result;
1209+
if (target.GetMetaObject(Expression.Constant(target)).TryDeleteIndex(indices, out result))
1210+
{
1211+
return result;
1212+
}
1213+
1214+
throw new InvalidOperationException("Invalid dynamic element deletion");
1215+
}
1216+
1217+
/// <summary>
1218+
/// Casts a dynamic host object to its static type.
1219+
/// </summary>
1220+
/// <param name="value">The object to cast to its static type.</param>
1221+
/// <returns>The specified object in its static type form, stripped of its dynamic members.</returns>
1222+
/// <remarks>
1223+
/// A dynamic host object that implements <see cref="IDynamicMetaObjectProvider"/> may have
1224+
/// dynamic members that override members of its static type. This function can be used to
1225+
/// gain access to type members overridden in this manner.
1226+
/// </remarks>
1227+
public object toStaticType(IDynamicMetaObjectProvider value)
1228+
{
1229+
return HostItem.Wrap(GetEngine(), value, HostItemFlags.HideDynamicMembers);
1230+
}
1231+
10031232
// ReSharper restore InconsistentNaming
10041233

10051234
#endregion

ClearScript/HostItem.InvokeMethod.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,15 +175,18 @@ private MethodBindResult BindMethod(string name, Type[] typeArgs, object[] args,
175175

176176
private MethodBindResult BindMethodInternal(string name, Type[] typeArgs, object[] args, object[] bindArgs)
177177
{
178+
// create C# member invocation binder
178179
const CSharpBinderFlags binderFlags = CSharpBinderFlags.InvokeSimpleName | CSharpBinderFlags.ResultDiscarded;
179180
var binder = Binder.InvokeMember(binderFlags, name, typeArgs, accessContext ?? engine.AccessContext, CreateArgInfoEnum(bindArgs));
180181

182+
// perform default binding
181183
var binding = DynamicHelpers.Bind((DynamicMetaObjectBinder)binder, target, bindArgs);
182184
var rawResult = (new MethodBindingVisitor(target.InvokeTarget, name, binding.Expression)).Result;
183185

184186
var result = MethodBindResult.Create(name, rawResult, target, args);
185187
if (!(result is MethodBindSuccess) && !(target is HostType) && target.Type.IsInterface)
186188
{
189+
// binding through interface failed; try base interfaces
187190
foreach (var interfaceType in target.Type.GetInterfaces())
188191
{
189192
var tempTarget = HostObject.Wrap(target.InvokeTarget, interfaceType);
@@ -196,6 +199,17 @@ private MethodBindResult BindMethodInternal(string name, Type[] typeArgs, object
196199
return tempResult;
197200
}
198201
}
202+
203+
// binding through base interfaces failed; try System.Object
204+
var objectTarget = HostObject.Wrap(target.InvokeTarget, typeof(object));
205+
binding = DynamicHelpers.Bind((DynamicMetaObjectBinder)binder, objectTarget, bindArgs);
206+
rawResult = (new MethodBindingVisitor(target.InvokeTarget, name, binding.Expression)).Result;
207+
208+
var objectResult = MethodBindResult.Create(name, rawResult, target, args);
209+
if (objectResult is MethodBindSuccess)
210+
{
211+
return objectResult;
212+
}
199213
}
200214

201215
return result;

0 commit comments

Comments
 (0)