microsoft / ClearScript

A library for adding scripting to .NET applications. Supports V8 (Windows, Linux, macOS) and JScript/VBScript (Windows).
https://microsoft.github.io/ClearScript/
MIT License
1.79k stars 149 forks source link

ReadOnly ScriptMember still writable according to getOwnPropertyDescriptor #585

Closed lukewarlow closed 3 months ago

lukewarlow commented 4 months ago

When setting a HostObject's property to be readonly to script, the descriptor from getOwnPropertyDescriptor() still says that the property is writable?

using Microsoft.ClearScript;
using Microsoft.ClearScript.V8;

namespace WebPlatform;

class Program
{
    class Foo
    {
        [ScriptMember(ScriptAccess.ReadOnly)]
        public int Prop { get; }
    }

    class JSConsole
    {
        public void log(object param)
        {
            Console.WriteLine(param.ToString());
        }
    }

    static void Main(string[] args)
    {
        var engine = new V8ScriptEngine();
        engine.AccessContext = typeof(Program);
        engine.AddHostObject("foo", new Foo());
        engine.AddHostObject("console", new JSConsole());
        engine.Execute("const descriptor = Object.getOwnPropertyDescriptor(foo, 'Prop'); console.log(descriptor.writable); foo.Prop = 2;");
    }
}

Results in

True
Unhandled exception. Microsoft.ClearScript.ScriptEngineException: Error: The property is read-only
 ---> Microsoft.ClearScript.ScriptEngineException: Error: The property is read-only
 ---> System.UnauthorizedAccessException: The property is read-only

I would have expected descriptor.writable to be false?

I would also expect the exception to be a TypeError?

Is there a way to define a property as being non-writable in the javascript sense using attributes like this?

ClearScriptLib commented 4 months ago

Hi @lukewarlow,

I would have expected descriptor.writable to be false?

Host objects always return { writable: true, enumerable: true, configurable: true } or undefined for Object.getOwnPropertyDescriptor. The interceptor that V8 invokes for that function currently checks only whether an accessible .NET member with the specified name exists.

The problem is that the JavaScript and .NET type systems are very different. There are .NET scenarios that JavaScript descriptors don't cover – e.g., write-only properties – and cases where a .NET property's meta-information can't be determined upfront – e.g., situations involving custom setters and dynamic objects.

I would also expect the exception to be a TypeError?

No, ClearScript maps all .NET exceptions to JavaScript's Error.

Is there a way to define a property as being non-writable in the javascript sense using attributes like this?

No, but you can use the facade pattern or JavaScript's Proxy to make host objects behave more like JavaScript objects in the script environment.

Good luck!

ClearScriptLib commented 3 months ago

Please reopen this issue if you have additional thoughts, findings, or questions about this topic. Thanks!