Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to pass ClrType to module for create instance in script #51

Closed
ZverGuy opened this issue Aug 7, 2022 · 13 comments
Closed

How to pass ClrType to module for create instance in script #51

ZverGuy opened this issue Aug 7, 2022 · 13 comments

Comments

@ZverGuy
Copy link
Contributor

ZverGuy commented Aug 7, 2022

Hello again
i am trying to implement "moduleBuilder" like jint

   _context.RegisterModule("test", builder =>
            {
                builder.ExportType<TestClass>()
                    .ExportFunction("testFunc", TestMethod);
            } );

And when i try to create TestClass instance

 var barfromclass = await _context
                .RunScriptAsync("import {TestClass} from \"test\"\n export default new TestClass();",
                    String.Empty) as JSObject; 
            Assert.AreEqual("bar", barfromclass["Foo"].InvokeFunction(new Arguments()));

I am get exception

YantraJS.Core.JSException: undefined is not a function 

TestClass implementation

 public class TestClass
    {
        public string Foo() => "bar";
    }

ModuleBuilder implementation

public class ModuleBuilder
{
    private readonly KeyString _moduleName;
    private readonly JSModuleContext _moduleContext;
    private readonly JSObject _exportobj;

    internal ModuleBuilder(KeyString moduleName,JSModuleContext moduleContext)
    {
        _moduleName = moduleName;
        _moduleContext = moduleContext;
        _exportobj = new JSObject();
    }

    public ModuleBuilder ExportType<T>()
    {
        string name = typeof(T).Name;
        var type = ClrType.From(typeof(T));
        _exportobj[name] = type;
        return this;
    }

    public ModuleBuilder ExportValue(in KeyString name, object value)
    {
        _exportobj[name] = value.Marshal();
        return this;
    }

    public ModuleBuilder ExportFunction(in KeyString name, JSFunctionDelegate func)
    {
        _exportobj[name] = new JSFunction(func);
        return this;
    }

    internal void Build()
    {
        _moduleContext.RegisterModule(_moduleName, _exportobj);
    }

/// in JSModuleContext

 public void RegisterModule(in KeyString name, Action<ModuleBuilder> builder)
        {
            var modulebuilder = new ModuleBuilder(name ,this);
            builder.Invoke(modulebuilder);
            modulebuilder.Build();
        }

Maybe you can help what i do wrong?

upd some intresting information

ClrType return function() { [clr-native]}

изображение

On runing this return a VoidValueTask
изображение

When i call testFunc() this is called but dont return value

var barfromclass = await _context
                .RunScriptAsync("import {testFunc} from \"test\"\n export default testFunc();",
                    String.Empty);

изображение
изображение

@ZverGuy
Copy link
Contributor Author

ZverGuy commented Aug 7, 2022

https://github.com/ZverGuy/yantra/commits/main if you want test - check my repo

I really hope that you can help, because I do not know why this is happening

@ackava
Copy link
Contributor

ackava commented Aug 8, 2022

You can use,

context.RegisterModule("test", ClrType.From(typeof(YourClrClass));

You can make module builder as follow,

public class ModuleBuilder {

   private List<(string name,Type type)> exportedTypes = new ();

   public ModuleBuilder Export<T>(string name = null) {
         exportedTypes.Add((name ?? typeof(T).Name, typeof(T)));
         return this;
   }

   public JSModuleContext Builder(JSModuleContext context) {
        foreach(var (name,type) in exportedTypes) {
              var exports = new JSObject();
              // currently YantraJS does not support synthetic default imports
              // so we should add "default" property in exports
              exports[KeyStrings.@default] = ClrType.From(type);
              context.RegisterModule(name, exports);
        }
   }

}

public static class ModuleBuilderExtensions {

        public static JSModuleContext Build(this JSModuleContext context, Action<ModuleBuilder> builder) {
               var mb = new ModuleBuilder();
               builder(mb);
               return mb.Build(context);
        }

}

@ZverGuy
Copy link
Contributor Author

ZverGuy commented Aug 8, 2022

Big thanks

expect my pull request soon

@ZverGuy
Copy link
Contributor Author

ZverGuy commented Aug 9, 2022

Check my pull request

@ackava
Copy link
Contributor

ackava commented Aug 9, 2022

You forgot to set default. Please note, we haven't added support for synthetic default modules like node, so as per ES6 we need to set default on exports to support "import test from .... " syntax. We are working on synthetic default module support.

Instead of JSFunction, use JSFunctionDelegate as JSFunction needs to be created per JSContext instance. I have added following code, which works correclty.

public class ModuleBuilder {

   private List<(string name,object value)> exportedObjects = new ();

   public ModuleBuilder ExportType<T>(string name = null) {
         exportedObjects .Add((name ?? typeof(T).Name, typeof(T)));
         return this;
   }

   public ModuleBuilder Export(string name, object value) {
         exportedObjects .Add((name , value));
         return this;
   }

   public ModuleBuilder ExportFunction(string name, JSFunctionDelegate @delegate) {
         exportedObjects .Add((name , @delegate));
         return this;
   }

   public JSModuleContext Builder(JSModuleContext context) {
        foreach(var (name,value) in exportedTypes) {
              var exports = new JSObject();
              var r = JSUndefined.Value;
              switch(value) {
                   case Type type:
                         r = ClrType.From(type);
                         break;
                   case JSFunctionDelegate @delegate:
                         r = new JSFunction(name, @delegate);
                         break;
                   case JSValue jv:
                         r = jv;
                         break;
                   default:
                         r = ClrProxy.Marshal(value);
                         break;
              }

              // currently YantraJS does not support synthetic default imports
              // so we should add "default" property in exports
              exports[KeyStrings.@default] = r;
              context.RegisterModule(name, exports);
        }
   }

}

public static class ModuleBuilderExtensions {

        public static JSModuleContext Build(this JSModuleContext context, Action<ModuleBuilder> builder) {
               var mb = new ModuleBuilder();
               builder(mb);
               return mb.Build(context);
        }

}

@ZverGuy
Copy link
Contributor Author

ZverGuy commented Aug 9, 2022

but there is a flaw in your method. each exported object has its own module. I want to do for example

import {TestClass, testFunc} from 'test'

But with your method i cant do that
Only

import TestClass from 'TestClass'
import testFunc from 'testFunc'

@ackava
Copy link
Contributor

ackava commented Aug 9, 2022

public class ModuleBuilder {

   private readonly string moduleName;

   public ModuleBuilder(string moduleName) {
        this.moduleName = moduleName;
   }

   private List<(KeyString name,object value)> exportedObjects = new ();

   public ModuleBuilder ExportType<T>(string name = null) {
         exportedObjects .Add((name ?? typeof(T).Name, typeof(T)));
         return this;
   }

   public ModuleBuilder Export(string name, object value) {
         exportedObjects .Add((name , value));
         return this;
   }

   public ModuleBuilder ExportFunction(string name, JSFunctionDelegate @delegate) {
         exportedObjects .Add((name , @delegate));
         return this;
   }

   public JSModuleContext Builder(JSModuleContext context) {
        var exports = new JSObject();
        foreach(var (name,value) in exportedTypes) {
              var r = JSUndefined.Value;
              switch(value) {
                   case Type type:
                         r = ClrType.From(type);
                         break;
                   case JSFunctionDelegate @delegate:
                         r = new JSFunction(name, @delegate);
                         break;
                   case JSValue jv:
                         r = jv;
                         break;
                   default:
                         r = ClrProxy.Marshal(value);
                         break;
              }

              exports[name] = r;
        }
        context.RegisterModule(moduleName, exports);
   }

}

public static class ModuleBuilderExtensions {

        public static JSModuleContext Build(this JSModuleContext context string moduleName, Action<ModuleBuilder> builder) {
               var mb = new ModuleBuilder(moduleName);
               builder(mb);
               return mb.Build(context);
        }

}

Usage

   context.Build("moduleName", (mb) => {
        mb.ExportType<SomeClrType>();
        mb.ExportFunction("SomeFunction", (in Arguments a) => JSUndefined.Value);
   });
   import { SomeClrType, SomeFunction } from "moduleName";

@ZverGuy
Copy link
Contributor Author

ZverGuy commented Aug 9, 2022

i made simillar
Its ok? Can be merged?
(Not used boolean and commented code i delete before merge)

public class ModuleBuilder
{
    private List<(string name,object value)> exportedObjects = new ();
    private string _moduleName;
    private readonly bool _enableSyntheticDefaultModules;

    public ModuleBuilder ExportType<T>()
    {
        exportedObjects.Add((typeof(T).Name, typeof(T)));
        return this;
    }

    public ModuleBuilder(string moduleName, bool enableSyntheticDefaultModules = false)
    {
        _moduleName = moduleName;
        _enableSyntheticDefaultModules = enableSyntheticDefaultModules;
    }

    public ModuleBuilder ExportType(Type type, string name = null)
    {
        exportedObjects.Add((type.Name, type));
        return this;
    }

    public ModuleBuilder ExportValue(string name, object value)
    {
        exportedObjects.Add((name, value.Marshal()));
        return this;
    }

    public ModuleBuilder ExportFunction(string name, JSFunctionDelegate func)
    {
        exportedObjects.Add((name, new JSFunction(func)));
        return this;
    }
    public void AddModuleToContext(JSModuleContext context)
    {
        JSObject globalExport = new JSObject();
        foreach ((string name, object value)  in exportedObjects)
        {
            // if (_enableSyntheticDefaultModules)
            // {
            //     //Used for make Sythetic default import work around
            //     JSObject localexport = new JSObject();
            //     localexport[KeyStrings.@default] =  value switch
            //     {
            //         Type type => ClrType.From(type),
            //         JSFunctionDelegate @delegate => new JSFunction(@delegate),
            //         JSValue jsValue => globalExport[name] = jsValue,
            //         _ => ClrProxy.Marshal(value)
            //     };
            //     context.RegisterModule(name, localexport);
            // }
            
            globalExport[name] = value switch
            {
                Type type => ClrType.From(type),
                JSFunctionDelegate @delegate => new JSFunction(@delegate),
                JSValue jsValue => globalExport[name] = jsValue,
                _ => ClrProxy.Marshal(value)
            };
        }
        context.RegisterModule(_moduleName, globalExport);
        
    }
}

In JSModuleContext

 public void CreateModule(string moduleName,bool enableSyntheticDefaultImport, Action<ModuleBuilder> builder)
        {
            var mb = new ModuleBuilder(moduleName, enableSyntheticDefaultImport);
            builder(mb); 
            mb.AddModuleToContext(this);
        }

@ackava
Copy link
Contributor

ackava commented Aug 9, 2022

Why can't you use ModuleBuilder in your own code? Since we haven't finalized api for synthetic defaults, we don't want to add ModuleBuilder now.

@ZverGuy
Copy link
Contributor Author

ZverGuy commented Aug 9, 2022

Okay
But curently i fix stuff with "synthetic default import" and its works (if i understand your issue)

@ackava
Copy link
Contributor

ackava commented Aug 12, 2022

@ZverGuy Your code will only work inside ModuleBuilder where else we are planning to support CSX modules and may be some other languages as well, so I need to understand the formal specification of ES6 and I want to implement it correct way.

@ZverGuy
Copy link
Contributor Author

ZverGuy commented Aug 12, 2022

@ZverGuy Your code will only work inside ModuleBuilder where else we are planning to support CSX modules and may be some other languages as well, so I need to understand the formal specification of ES6 and I want to implement it correct way.

I move ModuleBuilder to extension methods and other package

I go to test modulebuilder with csx module loader for check if something is broken or not

@ZverGuy
Copy link
Contributor Author

ZverGuy commented Aug 12, 2022

@ZverGuy Your code will only work inside ModuleBuilder where else we are planning to support CSX modules and may be some other languages as well, so I need to understand the formal specification of ES6 and I want to implement it correct way.

Hmm
Maybe i implement "ExportCSXModule" Method?

@ZverGuy ZverGuy closed this as completed Aug 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants