Cannot get property of a derived class

Jan 12, 2014 at 3:32 PM
Edited Jan 12, 2014 at 3:35 PM
Consider I have the following classes
    public class Element
    {
        public Location Location { get; set; }
    }

    public class Location
    {
        public string Name { get; set; }
    }

    public class LocationPoint : Location
    {
        public string Point { get; set; }
    }
var e = new Element
{
    Location = new LocationPoint
    {
        Name = "N",
        Point = "X"
    }
};

var engine = new V8ScriptEngine();
engine.AddHostObject("e", e);
Console.WriteLine(engine.Evaluate("e.Location.Point"));
Point value will be undefined. It seems like Clearscript uses reflection to get the property for "Element" but in this case, the property "Location" is not a "Location" but it is a derived class "LocationPoint".
Jan 12, 2014 at 3:46 PM
Here's a quick hack, in HostItem class, change
target.Type.GetScriptableProperty(name, invokeFlags, bindArgs); 
to
target.Target.GetType().GetScriptableProperty(name, invokeFlags, bindArgs);
That way, it will get the property from the derived class instead.
Coordinator
Jan 12, 2014 at 5:46 PM
Edited Jan 13, 2014 at 2:29 AM
Hi ravetam!

Great question. This may be an example of ClearScript behaving a little too much like C# :)

The expression you're evaluating, e.Location.Point, doesn't compile in C#, and it doesn't work in script code for the same reason. You need to cast e.Location to LocationPoint.

Here's how to do the cast in script code:
engine.AddHostObject("host", new HostFunctions());
engine.AddHostType("LocationPoint", typeof(LocationPoint));

engine.AddHostObject("e", e);
Console.WriteLine(engine.Evaluate("host.cast(LocationPoint, e.Location).Point"));
If you're wondering why ClearScript exposes the declared type of the property instead of the runtime type of its value, it has to do with preserving static type information in order to provide C#-like method binding. Without this behavior, some things might be easier to do in script code, but you'd lose the ability to access .NET members in many scenarios. Your proposed fix, for example, breaks 47 ClearScript tests :)

Nevertheless, ClearScript does provide a way to override its default behavior selectively:
public class Element
{
    [ScriptMember(ScriptMemberFlags.ExposeRuntimeType)]
    public Location Location { get; set; }
}
With the Element class defined this way, no cast is necessary in script code.

Thanks, and good luck!
Jan 13, 2014 at 1:52 PM
Thanks for the detailed explanation. I should have run the unit test first.

The second solution seems to work but the problem is, the class library has to reference Clearscript. If I were to disable this check at all (IsRestrictedForScript always returns false), would I run into any issue? I ran all the unit test and there's no error at all. And it seems to ran a lot faster because it is not reading the attribute from the property.
Coordinator
Jan 13, 2014 at 6:10 PM
If I were to disable this check at all (IsRestrictedForScript always returns false), would I run into any issue?

It depends on the design of the .NET API you're calling from script code. In general you'll probably be OK.

The default behavior is aligned with C#. Where C# requires a cast, so does script code. Your change will allow you to avoid casting in some situations, but it may also force you to cast in places where C# doesn't.

I ran all the unit test and there's no error at all.

You're right, and that's something we need to fix.

Cheers!
Jan 14, 2014 at 2:23 PM
Actually, I wanted it to look more "Javascript" coding style. Anyway, from my unit test, my script still works after the changes.

Thanks again. I'll keep "the casting issue" in mind.
Coordinator
Jan 14, 2014 at 3:50 PM
Right, automatic type restriction is a relatively new feature in ClearScript, designed to facilitate access to esoteric .NET APIs. We agree that for simple APIs that rely on polymorphism, turning it off may make sense. Maybe we'll add a flag for that, but in the meantime, your IsRestrictedForScript patch should be perfectly safe.
Jan 15, 2014 at 2:49 PM
Sounds great