This project has moved. For the latest updates, please go here.

for/in loop in JScript standards mode

Sep 19, 2015 at 6:14 PM
It appears that the for/in loop is not working when JScript engine is initialized with WindowsScriptEngineFlags.EnableStandardsMode flag.

I tried adding PropertyBag and ExpandoObject as host objects.

I noticed that there is a ScriptEngineException when executing for/in loop on either of the two objects which has HResult of 0x800a0005 (Invalid procedure call or argument).

Without WindowsScriptEngineFlags.EnableStandardsMode flag everything works without exceptions.

I am developing on Windows 10 with ClearScript 5.4.3

Here is the sample program:
        static void Main(string[] args)
        {
            var engine = new JScriptEngine(WindowsScriptEngineFlags.EnableStandardsMode);
            engine.AddHostType("Console", typeof(Console));

            dynamic expandoObj = new ExpandoObject();
            expandoObj.testProp = "expando testProp Text";
            engine.AddHostObject("expandoObj", expandoObj);

            var propertyBag = new PropertyBag();
            propertyBag["testProp"] = "property bag testProp Text";
            engine.AddHostObject("propertyBagObj", propertyBag);

            engine.Execute(@"Console.WriteLine('expandoObj.testProp=' + expandoObj.testProp);");
            engine.Execute(@"Console.WriteLine('propertyBagObj.testProp=' + propertyBagObj.testProp);");
            engine.Execute(@"Console.WriteLine('');");
            engine.Execute(@"for (var item in propertyBagObj) Console.WriteLine('propertyBagObj[' + item + ']=' + propertyBagObj[item]);");
            engine.Execute(@"Console.WriteLine('');");
            engine.Execute(@"for (var item in expandoObj) Console.WriteLine('expandoObj[' + item + ']=' + expandoObj[item]);");

        }
Coordinator
Sep 21, 2015 at 2:42 PM
Hi frolovm,

You're absolutely right. The for..in statement is broken for host objects in Standards Mode. Unfortunately this appears to be a JScript bug and therefore isn't likely to be fixed.

Consider however that for..in support is really only useful for IPropertyBag instances. All other host objects include instance methods, extension methods, and other undesirable members in the enumeration. This is by design, as some script engines (including JScript) don't have enumeration options for host object members.

There are several ways to enumerate host object members that work in Standards Mode and don't have the problem mentioned above. JScript's Enumerator class is one possibility:
engine.Execute(@"
    for (var e = new Enumerator(expandoObj); !e.atEnd(); e.moveNext()) {
        var item = e.item();
        Console.WriteLine('{0} = {1}', item.Key, item.Value);
    }
");
Unfortunately this currently doesn't work with IPropertyBag; that's a ClearScript bug that we'll fix in the next point release.

Another possibility might be to expose a JavaScript-friendly way to use .NET enumeration:
public static class IEnumerableExtensions {
    public static void @foreach(this IEnumerable source, dynamic callback) {
        foreach (var item in source) {
            callback(item);
        }
    }
}
and then:
engine.AddHostType(typeof(IEnumerableExtensions));
engine.Execute(@"
    expandoObj.foreach(function(item) {
        Console.WriteLine('{0} = {1}', item.Key, item.Value);
    });
");
Finally, for scriptable name-value collections, you can always use native JavaScript objects instead of host objects:
dynamic someObj = engine.Evaluate("({})");
someObj.foo = 123;
someObj.bar = 456;
engine.Script.someObj = someObj;
Thanks for reporting this issue, and good luck!
Sep 21, 2015 at 10:14 PM
Thanks, it seems like for my case I would have to use your last suggestion.

We have a set of Javascripts that work with Google Chrome and Firefox and we would rather not modify them with non-standard code.

What makes you think this is a problem with JScript? It is able to enumerate JScript objects, I would imagine it is using some kind of IDispatch/Ex capability. Could it be looking for a "hidden" property or method of an object to perform its for...in enumeration? Perhaps this is a matter of supplying JScript with what it is looking for?
Coordinator
Sep 23, 2015 at 5:00 PM
Hi again,

We have a set of Javascripts that work with Google Chrome and Firefox and we would rather not modify them with non-standard code.

Just curious, why are you using JScript? V8 provides much better performance and Chrome compatibility, and it isn't affected by the issue you discovered.

What makes you think this is a problem with JScript? It is able to enumerate JScript objects, I would imagine it is using some kind of IDispatch/Ex capability.

We've investigated this internally and found some surprising things. When Standards Mode is enabled, JScript subtly breaks the IDispatchEx contract in order to pass some extra data to the host for certain calls. Hosts such as Internet Explorer and JScript itself can handle this, but the CLR cannot, and ClearScript relies on the CLR's built-in IDispatchEx implementation. The for..in loop isn't the only feature affected by this; host property deletion also fails in Standards Mode, and there may be others.

It may be possible to work around this problem. We're still investigating.

Thanks again!
Sep 23, 2015 at 7:28 PM
ClearScript wrote:
Hi again,

We have a set of Javascripts that work with Google Chrome and Firefox and we would rather not modify them with non-standard code.

Just curious, why are you using JScript? V8 provides much better performance and Chrome compatibility, and it isn't affected by the issue you discovered.

One concern is licensing. It may be difficult to get it approved.