sebastienros / jint

Javascript Interpreter for .NET

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Indexer does not work anymore

SebastianStehle opened this issue · comments

Version used

Before: 3.0.0-beta-2049
Now: 3.0.0-beta-2055

Describe the bug

I am having issues to reproduce this problem, but I think I have some kind of explanation.

I have a custom JSON type in my application and asset metadata as Dictionary<string, JsonValue>. To make it possible to set metadata in my script I have a custom dictionary that makes these conversions, e.g.

public sealed class AssetMetadataWrapper : IDictionary<string, object?>
{
    private readonly Dictionary<string, JsonValue> metadata;

    public object? this[string key]
    {
        get => metadata[key];
        set => metadata[key] =JsonValue.Create(value);
    }
    public AssetMetadataWrapper(Dictionary<string, JsonValue> metadata)
    {
    }

All values are passed to the script via a context object, which basically looks like this:

public sealed class AssetCommandScriptVars : Dictionary<string, object>
{
    public AssetMetadata Metadata
    {
        set => this["Metadata"] = new AssetMetadataWrapper(value);
    }

The test is very simple, I just call

"ctx.command.metadata['foo'] = 42;"

And I get the following error:

    Squidex.Infrastructure.Validation.ValidationException : common.jsError.
    ---- System.Reflection.TargetException : Object does not match target type.
 MethodInvokerCommon.ValidateInvokeTarget(Object target, MethodBase method)
 RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
 MethodBase.Invoke(Object obj, Object[] parameters)
 IndexerAccessor.DoSetValue(Object target, Object value)
 ReflectionAccessor.SetValue(Engine engine, Object target, JsValue value)

I was debugging it and found out that the indexer in the ÌndexerAccessor` does not point to my wrapper, but to the wrapped metadata. If I rename the property it does not happen. It has also something to do with the custom dictionaries.

Btw: If you have a solution how to get rid of the wrapper, plz tell me. There is no converter from jint to clr, right?.

To Reproduce

I have a code sample here: https://gist.github.com/SebastianStehle/039ef22e5e5042b45652649f761e1417

It is much more code than I was hoping for, but when I use normal dictionaries it does not happen.

Expected behavior

A clear and concise description of what you expected to happen.

Additional context

Add any other context about the problem here.

I think Jint gets quite confused because of the exposed Metadata property an the indexer that's in the base class. Something like this might remedy the situation:

class MyContext : ScriptVars
{
    public MyContext(Dictionary<string, int> metadata)
    {
        SetInitial(new AssetMetadataWrapper(metadata), "metadata");
    }
}

So removing the property and explicitly using the indexer for accesses and not shadowing it with another property.

Yes, but it is a little bit more complex than that.. but I solved it with a custom converter now and got rid of the wrapper. But it was working in previous versions.

Another option is have a member filter, this is quite the special case of hiding of members so a PR would be great if you want to fine-tune the resolution logic (should not consider seen dictionary as it's going into set-only member).

Sorry, I am so busy with my other open source projects at the moment ;)

So should we close?

As you wish. I think it is a bug, but only a very little one.

I was able to improve the resolution for this particular case after two hours of head-scratching, but I can think many others where such shadowing with different indexer signatures and get/set abilities will cause trouble. So I would really avoid such constructs.