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.