sq / JSIL

CIL to Javascript Compiler
http://jsil.org/
Other
1.73k stars 242 forks source link

JsObject API #532

Open iskiselev opened 10 years ago

iskiselev commented 10 years ago

I suggest to introduce JsObject API, that will allow easier interop with JS, then using Verbutium without usage of dynamic. Suggested API:

public abstract class JsObject
{
    public abstract JsObject this[string name] { get; set; }

    public abstract JsObject Get(string name);
    public abstract T Get<T>(string name);
    public abstract void Set(string name, object value);

    public abstract JsObject Call(string name, params object[] args);
    public abstract T Call<T>(string name, params object[] args);
    public abstract JsObject Create(string name, params object[] args);

    public abstract T Cast<T>();
}

public abstract class JsFunction : JsObject
{
    public abstract JsObject Invoke(params object[] args);
    public abstract T Invoke<T>(params object[] args);
    public abstract JsObject InvokeCreate(params object[] args);
}

Additionally we can overload some operators in JsObject. Initial implementation could be done with JsReplace attribute, but for proper implementation we should implement it inside JSIL transfromations logic - as we should detect creating new array in params calls and replace it with simple method call. Also we may discuss, if JsFunction should catch original this context or not.

EDITED: Suggested API changed based on @kg feedback

iskiselev commented 10 years ago

This issue is connected with #531.

kg commented 10 years ago

JsFunction.Call should be named JsFunction.Invoke. Otherwise this is ambiguous:

JsObject o; JsFunction f;

o.Call("x", 1);
o.Call("y");

f.Call();
f.Call(1);

f.Call("y"); // ????????
kg commented 10 years ago

JsObject.Cast's intent is ambiguous; it is unclear whether it is a conversion or an 'I promise this value is of type T' operation. For JsObject I would instead do something like:


public abstract T AssumeType<T>();
public abstract T As<T> () where T : class;
public abstract T Cast<T> ();
public abstract bool Is<T> ();

Sadly we can't support as/is/(T) on JsObject since the compiler will yell that the conversions are impossible :-(

kg commented 10 years ago

Also worthwhile to add:

public abstract JsObject this[int index] { get; set; }
iskiselev commented 10 years ago

Also it may be good idea add to JsObject for iterating over keys in object.:

public abstract IEnumerable<string> In()
kg commented 10 years ago

Ah, yes. Though I would probably name that one Keys. Good point either way.

iskiselev commented 10 years ago

Could you define, how should Cast<T> work for JsObject per you example? My initial thought was only introducing it with AssumeType<T> semantics.

kg commented 10 years ago

It would generate a checked static cast at runtime - T.$Cast(X). Same for As and Is.

Bablakeluke commented 8 years ago

Hey guys, I've started on the above, specifically on the libraries rather than the compiler side; my goal at this point is to essentially produce the set of core lib DLLs based on the Mono ones using the above API. Quick overview:

The end result would be a set of "standalone" DLLs which could then be passed through the JSIL compiler.

iskiselev commented 8 years ago

@Bablakeluke, I'm not sure that fully understand your goals. Which Mono libraries you want to translate? mscorlib/System.Core/System? What do you want to achieve with it? I have successful experience of translating that libraries (really it works with MS .Net a little bit better for now). Main problem right now is conflicts of types from that assemblies and types that were already defined in JSIL Bootstap JS files. We can't live without bootstrap, but it is not easy task mix them with translated parts (some work for it is inside #857). External implementation for most classes in BCL library is not worth problem - as (1) this class could not be used a lot or (2) already has implementation in JSIL Bootstrap.

Probably I just not fully understand your desires. If you describe them more (probably with examples) I may suggest you some better options :)

Bablakeluke commented 8 years ago

Sure, I'll try and describe it a little better: In short, Reflection.Emit support. This single feature requires a completely different direction and, in my case at least (a moddable game currently in the Unity web player with its own custom scripting languages), is vital to have. Ideally, I want to leverage as much existing code as possible. That's where the Mono libraries come in - All of them! The point of view is that the runtime must be able to load a bytecode translated from emit calls anyway, so it might as well load the libraries in this way too. That allows it to keep the compactness of streaming IL vs streaming potentially enormous amounts of pre-translated .NET IL in Javascript.

So, a few things about this approach. Keeping in line with reusing as much code as possible, the actual bytecode and files being streamed in would actually be WebAssembly but with a small GC extension (which is coming in the distant future of WebAssembly anyway, and the WebIL project will essentially be making proposals on how it should operate). This avoids needing to implement a complete .NET runtime in Javascript and it'll be accelerated by all major browsers shortly too. A quick summary at this point, then, is like this:

Mono DLLs -> WebAssembly files (+GC calls) -> stream to browser -> Load on a runtime

Next, ideally, that runtime is as small as it can possibly be. Totally free from any particular languages/ frameworks like .NET, if possible - allowing it to leverage the existing JS engines. That means all the bootstrap-like functionality needs to be contained within those special WebAssembly files, which are being derived from ordinary DLLs. This is where JSObject comes in.

Getting JS calls into the Mono DLLs

The Mono DLLs of course contain a bunch of extern methods - the stubs which point at the functionality implemented in the bootstrap/ runtime. However, I need to implement them inside the library itself - get JS calls into the body of the methods. That process works like this:

A Mono DLL -> Get all the extern/ InternalCall methods -> Generate a bunch of C# files with those methods in them -> Implement the methods in C# with JSObject calls -> Compile them into a DLL -> Merge this DLL with the original Mono one, overwriting the original extern call. From there, the resulting DLL is the one which then goes ahead and gets converted into a WebAssembly file.

(It's worth pointing out here that the Mono DLL is used rather than the sources as it provides a nice wall between Mono/WebIL)

So, time for a bit of an example! At the moment, the generation/ compilation/ merge of the C# files is working, and Socket seems like a nice place to start with an example as feature wise, it's about as awkward as they get (ironically, inside a web browser). In this case, the implementation would be the original protocol over websocket or WebRTC, and it's just going to be a very basic overview for now. Looking in the C# files generated from the Mono DLL, there's this method:

private static void Connect_internal_real(IntPtr sock, SocketAddress sa, out int error){

throw new NotSupportedException();

}

First thing to note is JSObject and IntPtr are essentially the same thing. So, the body would look something like this:

private static void Connect_internal_real(IntPtr sock, SocketAddress sa, out int error){

// Give the compiler some context (this line isn't actually here in the final output - it's purely for the compile pipeline):
JSObject jsObject=new JSObject(sock);

// Get the port (3rd and 4th bytes are port, little endian, everything after is IP):
int port=sa.Item[2] | (sa.Item[3]<<8);

// Get the IP (v4 only here for now):
string ip=sa.Item[4]+"."+sa.Item[5]+"."+sa.Item[6]+"."+sa.Item[7];

// Create a websocket:
// In vanilla JS, this is equiv to new websocket("ws://"+ip+":"+port+"/");
JSObject webSocket=JS.Import("websocket").New("ws://"+ip+":"+port+"/");

// Other examples of the API:
// webSocket.url
// string url=webSocket["url"] as string;

// webSocket.close();
// webSocket.Invoke("close",n,params,would,go,here);

// Overwrite the thing at the original pointer:
jsObject.Set(webSocket);

}

The code above is of course rough, so only take it as an overview of the API. Importantly, it's all (probably!) valid C#, aka it will compile into a DLL. This method inside that DLL can then be used to overwrite the original Mono extern method with the same namespace and method name, creating a method which is now only dependent on the JSObject API. In short, the conversion to WebAssembly implements the relatively tiny API, resulting in a "standalone" WebAssembly file, without the need for built in methods. From there, the compiler can do all the hard work, stripping IL methods that are known to be not in use, or the pipeline could be used for other languages like Java too.

iskiselev commented 8 years ago

At least for initial implementation, I'd like to keep JS interop API as simple, as possible. My proposal for first cut:

    public abstract class JsObject
    {
        public abstract JsObject this[string name]
        {
            [JSReplacement("$this[$name]")]
            get;
            [JSReplacement("$this[$name] = $value")]
            set;
        }
    }

    public abstract class JsFunction
    {
        [JSReplacement("$this()")]
        public abstract JsObject Invoke();
        [JSReplacement("$this($arg0)")]
        public abstract JsObject Invoke<TArg0>(TArg0 arg0);
        [JSReplacement("$this($arg0, $arg1)")]
        public abstract JsObject Invoke<TArg0, TArg1>(TArg0 arg0, TArg1 arg1);
        [JSReplacement("$this($arg0, $arg1, $arg2)")]
        public abstract JsObject Invoke<TArg0, TArg1, TArg2>(TArg0 arg0, TArg1 arg1, TArg2 arg2);
        [JSReplacement("$this($arg0, $arg1, $arg2, $arg3)")]
        public abstract JsObject Invoke<TArg0, TArg1, TArg2, TArg3>(TArg0 arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3);
        [JSReplacement("$this($arg0, $arg1, $arg2, $arg3, $arg4)")]
        public abstract JsObject Invoke<TArg0, TArg1, TArg2, TArg3, TArg4>(TArg0 arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4);
        [JSReplacement("$this($arg0, $arg1, $arg2, $arg3, $arg4, $arg5)")]
        public abstract JsObject Invoke<TArg0, TArg1, TArg2, TArg3, TArg4, TArg5>(TArg0 arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5);
        [JSReplacement("$this($arg0, $arg1, $arg2, $arg3, $arg4, $arg5, $arg6)")]
        public abstract JsObject Invoke<TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6>(TArg0 arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6);
        [JSReplacement("$this($arg0, $arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7)")]
        public abstract JsObject Invoke<TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7>(TArg0 arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7);

        [JSReplacement("new $this()")]
        public abstract JsObject InvokeNew();
        [JSReplacement("new $this($arg0)")]
        public abstract JsObject InvokeNew<TArg0>(TArg0 arg0);
        [JSReplacement("new $this($arg0, $arg1)")]
        public abstract JsObject InvokeNew<TArg0, TArg1>(TArg0 arg0, TArg1 arg1);
        [JSReplacement("new $this($arg0, $arg1, $arg2)")]
        public abstract JsObject InvokeNew<TArg0, TArg1, TArg2>(TArg0 arg0, TArg1 arg1, TArg2 arg2);
        [JSReplacement("new $this($arg0, $arg1, $arg2, $arg3)")]
        public abstract JsObject InvokeNew<TArg0, TArg1, TArg2, TArg3>(TArg0 arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3);
        [JSReplacement("new $this($arg0, $arg1, $arg2, $arg3, $arg4)")]
        public abstract JsObject InvokeNew<TArg0, TArg1, TArg2, TArg3, TArg4>(TArg0 arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4);
        [JSReplacement("new $this($arg0, $arg1, $arg2, $arg3, $arg4, $arg5)")]
        public abstract JsObject InvokeNew<TArg0, TArg1, TArg2, TArg3, TArg4, TArg5>(TArg0 arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5);
        [JSReplacement("new $this($arg0, $arg1, $arg2, $arg3, $arg4, $arg5, $arg6)")]
        public abstract JsObject InvokeNew<TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6>(TArg0 arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6);
        [JSReplacement("new $this($arg0, $arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7)")]
        public abstract JsObject InvokeNew<TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7>(TArg0 arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7);
    }

    public static class JsObjectHelpers
    {
        [JSReplacement("$obj")]
        public static T AssumeType<T>(this JsObject obj)
        {
            throw new NotImplementedException();
        }

        [JSReplacement("$T.$$As($obj)")]
        public static T As<T>(this JsObject obj) where T : class
        {
            throw new NotImplementedException();
        }

        [JSReplacement("$T.$$Cast($obj)")]
        public static T Cast<T>(this JsObject obj)
        {
            throw new NotImplementedException();
        }

        [JSReplacement("$T.$$Is($obj)")]
        public static bool Is<T>(this JsObject obj)
        {
            throw new NotImplementedException();
        }
    }

Verbatium.Expression, JSGlobal, JSLocal will return JsObject. My next step will be intorducing JS implementation for all interop classes. With it Reflection and dynamic will work normally.

Do we really need any other methods for JsObject/JsFunction?

And now small test example:

          var obj = Verbatim.Expression("{prop: function() {this.inner=function() { this.func = function() {return 10.1 }}}}");

          var num = obj
                ["prop"].AssumeType<JsFunction>().InvokeNew()
                ["inner"].AssumeType<JsFunction>().InvokeNew()
                ["func"].AssumeType<JsFunction>().Invoke().Cast<int>() + 10;
          Console.WriteLine(num);
Bablakeluke commented 8 years ago

I would suggest that even that may be too detailed. So far the following has been very successful for coverage albeit still in very early stages (thus it's not been committed anywhere just yet):

using System;
using System.Collections;
using System.Collections.Generic;

namespace WebAPI{

    /// <summary>
    /// This represents an object from the DOM. This class is mostly a no-op; the runtime or compiler that
    /// uses this API implements the methods directly, emitting the equivelant Javascript.
    /// </summary>

    public class JsObject{

        public JsObject(string name){
        }

        /*
        public Type GetType(){

            // Here as a reminder only; runtime/compiler must implement this one (e.g. get the constructor prototype).

        }
        */

        /// <summary>For example, new Array() is implemented as Js.Global["Array"].New().</summary>
        public JsObject New(params object[] args){

            // No-op in this stub. Runtime/ compiler must implement this.
            throw new NotImplementedException();

        }

        /// <summary>True if the given key exists on this object.</summary>
        public bool Exists(string key){

            // No-op in this stub. Runtime/ compiler must implement this.
            throw new NotImplementedException();

        }

        /// <summary>Equivelant of the JS delete keyword. 
        /// Deletes the given property from the JS object this represents.</summary>
        public void Delete(string key){

            // No-op in this stub. Runtime/ compiler must implement this.
            throw new NotImplementedException();

        }

        /// <summary>All the keys in this object.</summary>
        public IEnumerable<string> Keys{
            get{

                // No-op in this stub. Runtime/ compiler must implement this.
                throw new NotImplementedException();

            }
        }

        /// <summary>Gets or sets a particular value on this object.</summary>
        public object this[string key]{

            get{

                // No-op in this stub. Runtime/ compiler must implement this.
                throw new NotImplementedException();

            }

            set{

                // No-op in this stub. Runtime/ compiler must implement this.
                throw new NotImplementedException();

            }

        }

        /// <summary>Gets or sets a particular value at the given index on this object.</summary>
        public object this[int index]{

            get{

                // No-op in this stub. Runtime/ compiler must implement this.
                throw new NotImplementedException();

            }

            set{

                // No-op in this stub. Runtime/ compiler must implement this.
                throw new NotImplementedException();

            }

        }

        /// <summary>Runs this DOM object as a method. 
        /// For example, Js.Global["console"]["log"].Invoke("Hello world!");</summary>
        public object Invoke(params object[] args){

            // No-op in this stub. Runtime/ compiler must implement this.
            throw new NotImplementedException();

        }

    }

}

Everything else is provided by the C# language itself the above is essentially a stub (along with another tiny file) which just makes the C# compiler happy. Usage is as follows:

JSObject websocket=Js.Global("websocket").New("wss://...");

websocket["onopen"]=delegate(JSObject e){
  // Open event
};

(websocket["send"] as JSObject).Invoke("hello!");

Useful side effects are things like strings and numbers are directly usable, i.e: int readyState=websocket["readyState"] as int;

For JSIL, once the above is compiled and the IL is being processed, the calls can fairly trivially be swapped for their equivalent Javascript. JSFunction seemingly isn't necessary either, as the usage here is to only satisfy the C# compiler; everything else can be derived (i.e. the user is using Invoke, therefore "send" can be expected to be a function).

We're in the process of swapping all extern/ internal calls in the Mono DLLs with normal C# code written using the above API, with the end goal being to make a DLL which has very few dependencies (i.e. this API, basic types and something which understands IL)

iskiselev commented 8 years ago

I have no objectives against Exists and Delete. I'm not sure if we really need Keys - you can achieve the same with other API:

var obj = Verbatim.Expression("{prop: 111, prop2: 222}");
foreach (string key in Verbatim.Expression("Object")["keys"].AssumeType<JsFunction>().Invoke(obj).AssumeType<IEnumerable>())
{
  Console.WriteLine(key + ": " + obj[key]);
}

I have strict objectives against Invoke/New with params object[] args - it will be impossible to implement if somebody will try to pass real array with content unpredictable on translation time, same as it will be impossible to implement on JS side for reflection/dynamic integration. It could be solved with Invoke/New that will get args (string key, params object[] args) - invoking it from parent object, so that we could use appropriate this if we would fallback to call through apply method. I'd prefer not add it at all - if you need call it with apply, do it explicitly - API already provide you with such option - and we will keep API simpler.

Same I have objectives against object this[int index] - it is mostly used for interactions with Arrays. Just use .AssumeType<JSObject[]>() for it.

Probably we don't need Cast, As, Is methods, but I like their syntax much more than parenthesis that would be used otherwise with native C# operators/cast. Need to be discussed a little bit more.

I'm not sure if we want extract Invoke/New to subclass. It a little bit more type-safe, but may end with lot more typing. On other hand it will be safe way to check if something is really function on JS side.

iskiselev commented 8 years ago

As I'd like remove params object[] args signatures from JsAPI, I'd like to discuss - how many parameters will be enough? 8? 16?

iskiselev commented 8 years ago

Also, I thought about Adapt:

public static class JsObjectHelpers
    {
        public static T Adapt<T>(this JsObject obj)
        {
            throw new NotImplementedException();
        }
    }

If T is interface, it will create adapter, that will redirects all interface methods calls inside JS object with preserving original interface method name, than Adapt<T> will be called on return value with T based on .Net interface method return type. If T is not interface type, it will work same as Cast<T> method.

UPDATE: Adapt method will be not changed on compile time, instead it will be fully JS-based implemented.

iskiselev commented 8 years ago

Next question, does anybody have any objectives if all JsAPI methods will throw NotSupportedException if they executed in .Net? Here I mean also Verbatium.Expression, BuiltIn.Local, BuiltIn.Global and so on.

iskiselev commented 8 years ago

Looks like it is impossible to create JS-based implementation for Verbatium.Expression, that will work in dynamic context same, as it will work in static context when translated. That's why I'd like just add some explicit error message in translation, if somebody will try use it in dynamic context.

Also, for better understand of type system and dynamic interop compatibility, all method of JSObject/JSFunction will be extension methods. We will lost indexer and need use Get/Set method instead.

iskiselev commented 8 years ago

Updated API proposal:

namespace JSIL {
    using System;

    using JSIL.Meta;

    public abstract class JsObject
    {
        //Should work same as Builtins.Global[key]
        public static JsObject Global(string key)
        {
            throw new NotSupportedException();
        }
    }

    public abstract class JsFunction
    {
    }

    public static class JsObjectHelpers
    {
        [JSReplacement("$target[$key]")]
        public static JsObject Get(this JsObject target, string key)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("($target[$key] = $value)")]
        public static void Set<TValue>(this JsObject target, string key, TValue value)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("($key in $target)")]
        public static bool In(this JsObject target, string key)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("(delete $target[$key])")]
        public static void Delete(this JsObject target, string key)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target[$key]()")]
        public static JsObject Call(this JsFunction target, string key)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target[$key]($arg1)")]
        public static JsObject Call<TArg1>(this JsFunction target, string key, TArg1 arg1)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target[$key]($arg1, $arg2)")]
        public static JsObject Call<TArg1, TArg2>(this JsFunction target, string key, TArg1 arg1, TArg2 arg2)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target[$key]($arg1, $arg2, $arg3)")]
        public static JsObject Call<TArg1, TArg2, TArg3>(this JsFunction target, string key, TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target[$key]($arg1, $arg2, $arg3, $arg4)")]
        public static JsObject Call<TArg1, TArg2, TArg3, TArg4>(this JsFunction target, string key, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target[$key]($arg1, $arg2, $arg3, $arg4, $arg5)")]
        public static JsObject Call<TArg1, TArg2, TArg3, TArg4, TArg5>(this JsFunction target, string key, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target[$key]($arg1, $arg2, $arg3, $arg4, $arg5, $arg6)")]
        public static JsObject Call<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6>(this JsFunction target, string key, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target[$key]($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7)")]
        public static JsObject Call<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7>(this JsFunction target, string key, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target[$key]($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8)")]
        public static JsObject Call<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8>(this JsFunction target, string key, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7, TArg8 arg8)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target.call(null)")]
        public static JsObject Invoke(this JsFunction target)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target.call(null, $arg1)")]
        public static JsObject Invoke<TArg1>(this JsFunction target, TArg1 arg1)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target.call(null, $arg1, $arg2)")]
        public static JsObject Invoke<TArg1, TArg2>(this JsFunction target, TArg1 arg1, TArg2 arg2)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target.call(null, $arg1, $arg2, $arg3)")]
        public static JsObject Invoke<TArg1, TArg2, TArg3>(this JsFunction target, TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target.call(null, $arg1, $arg2, $arg3, $arg4)")]
        public static JsObject Invoke<TArg1, TArg2, TArg3, TArg4>(this JsFunction target, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target.call(null, $arg1, $arg2, $arg3, $arg4, $arg5)")]
        public static JsObject Invoke<TArg1, TArg2, TArg3, TArg4, TArg5>(this JsFunction target, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target.call(null, $arg1, $arg2, $arg3, $arg4, $arg5, $arg6)")]
        public static JsObject Invoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6>(this JsFunction target, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target.call(null, $arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7)")]
        public static JsObject Invoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7>(this JsFunction target, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$target.call(null, $arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8)")]
        public static JsObject Invoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8>(this JsFunction target, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7, TArg8 arg8)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("new $target()")]
        public static JsObject New(this JsFunction target)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("new $target($arg1)")]
        public static JsObject New<TArg1>(this JsFunction target, TArg1 arg1)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("new $target($arg1, $arg2)")]
        public static JsObject New<TArg1, TArg2>(this JsFunction target, TArg1 arg1, TArg2 arg2)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("new $target($arg1, $arg2, $arg3)")]
        public static JsObject New<TArg1, TArg2, TArg3>(this JsFunction target, TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("new $target($arg1, $arg2, $arg3, $arg4)")]
        public static JsObject New<TArg1, TArg2, TArg3, TArg4>(this JsFunction target, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("new $target($arg1, $arg2, $arg3, $arg4, $arg5)")]
        public static JsObject New<TArg1, TArg2, TArg3, TArg4, TArg5>(this JsFunction target, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("new $target($arg1, $arg2, $arg3, $arg4, $arg5, $arg6)")]
        public static JsObject New<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6>(this JsFunction target, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("new $target($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7)")]
        public static JsObject New<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7>(this JsFunction target, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("new $target($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8)")]
        public static JsObject New<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8>(this JsFunction target, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7, TArg8 arg8)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$obj")]
        public static T AssumeType<T>(this JsObject obj)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$T.$$As($obj)")]
        public static T As<T>(this JsObject obj) where T : class
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$T.$$Cast($obj)")]
        public static T Cast<T>(this JsObject obj)
        {
            throw new NotSupportedException();
        }

        [JSReplacement("$T.$$Is($obj)")]
        public static bool Is<T>(this JsObject obj)
        {
            throw new NotSupportedException();
        }

        // Will wrap JS object in adapter, that implement interface T
        // If T is not interface, than work same as Cast 
        public static T Adapt<T>(this JsObject obj)
        {
            throw new NotSupportedException();
        }
    }
}

All functions here will have duplicated JS implementations, so that they will work through Reflection and in dynamic context.

UPDATE: Besides JSObject and JSFunction, only Services::Get will have JS implementation.

Attempt to get type ref to Builtins, JSGlobal, JSLocal, Verbatium will throw explicit errors on translation. So, that classes will be not accessible through reflection and in dynamic context.

iskiselev commented 8 years ago

@kg and @Bablakeluke, please review proposal changes in sq/JSIL.Meta#3. This changes will break backward compatibility with JS interop.

iskiselev commented 8 years ago

Though more about Adapt method. I don't see how it would solve problems of methods input args that are "typed JS objects". Probably will skip Adapt for now. We should return to it when we'll start .d.ts integration work.