We inherit the .NET type from the JavaScript object with overloads and private methods

    Yes, that’s just no tricks. This idea came to my head about two months ago in the process of pondering an article on Algorithms and solutions . .NET types in that engine are easy to use, but can it be vice versa ...

    A little bit about the engine
    In a previous article, I tried to write less about features and more about what might be useful in isolation from scripting in general, so that the article would be less like self-promotion. Literally, from the first comments, I realized that this was a mistake.
    Speed
    He is fast. Very fast. Over the past month, a lot of code has been shoveled over and a lot of special cases have been sorted out and conclusions made. For example, depending on how the function is written and what language tools are used, it can be called in one of more than 10 scenarios from the equivalent of inline to the fair allocation of memory for each variable and argument and full initialization of the context.
    Ease of integration
    The whole heavy "kitchen" to provide access to the types of platforms is hidden behind one modest function
    JSObject TypeProxy.Proxy(object)

    You simply give almost any object and assign the result to a variable
    var script = new Script(" megaObject.alert('Hello from javascript') ");
    script.Context.DefineVariable("megaObject")
                  .Assign(TypeProxy.Proxy(new { alert = new Action(x => MessageBox.Show(x)) });
    script.Invoke();
    

    For types that implement the IList interface, there is a special wrapper NiL.JS.Core.TypeProxing.NativeList that disguises such an object as a native js array.
    You can register a type constructor and create objects already at the time the script runs. A variable with the type name will be added.
    script.Context.AttachModule(typeof(System.Windows.Forms.Form));

    If you are too lazy to add types one at a time, you can add an entire namespace
    Context.GlobalContext.DefineVariable
                                ("forms") // имя переменной, через которую будет доступно пространство имён.
                                .Assign(new NamespaceProvider
                                    ("System.Windows.Forms")); // пространство имён, к которому будет осуществляться доступ.

    Not Only Execution
    Each syntax tree node is accessible from outside the assembly. You can implement your virtual machine, a translator into another language, a static analyzer (if there is not enough integration), or something else that your imagination is capable of. Each use of all variables stores a link to the corresponding descriptor, which can tell some information about it. For example, show all places of use that "survived" after optimization. A couple of weeks ago, the so-called Visitor was implemented with the help of which it is even easier to make all of the above.

    General scheme


    In order to inherit a type in the .NET platform, you need an assembly lying on the disk in which type information (metadata) is stored. But while the JS file is on disk, there are no types there. They will appear there only at runtime, when the logic of this script divides the functions into, in fact, functions and constructors. The solution to this problem was found almost immediately - I added a function to the global context that takes a constructor (“registerClass”). Thus, I, as it were, ask me to show for what js-functions it is worth generating metadata. However, this requires an idle run.
    After the execution is completed, using System.Reflection.Emit, a saved assembly is created in which one class is declared for each registered constructor, plus another static one that will store javascript code and run it the first time it is accessed. At this point, running registerClass () is already used to match the types in the assembly with the types in the script. All wrapper types are inherited from one type which describes the basic mechanisms of interaction. The composition of types is formed on the basis of what was found in the prototype constructor.
    function ctor() {
    }
    ctor.prototype // есть у каждой функции кроме некоторых встроенных
    

    Thus, if you add an non-enumerable property to the prototype, it will not fall into the metadata and become “private”.

    Great, now you can create JS objects as .NET objects, but where are the promised inheritance and overloads ?!

    This was decided easier. In order to understand which methods are overloaded, in the constructor of the base type, all functions in the type are passed (I understand that this is not a good place, but this is enough for the prototype implementation) and the ancestor of the type that declared them is checked. All overridden methods become declared in the descendant type. All methods that can be found by such a check are added to the JS type implementation. All.
    Now even new functions added will be available in js, they do not need to be overloaded.

    The code is not big, you can see it onGitHub

    P.S. This solution, at least at this stage, is not suitable for serious use. This is an experiment.

    Also popular now: